import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { AccountingPeriodPeriodType } from "../generated/enums";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  FiscalYearNotFoundError,
  NameRequiredError,
  InvalidDateRangeError,
  OverlappingPeriodError,
  DuplicatePeriodDatesError,
} from "../lib/errors.generated";

export interface CreateAccountingPeriodInput {
  companyId: string;
  fiscalYearId: string;
  name: string;
  startDate: Date;
  endDate: Date;
  periodType: AccountingPeriodPeriodType;
}

/**
 * Function: createAccountingPeriod
 *
 * Creates a period inside a fiscal year after validating company ownership,
 * date bounds, duplicate dates, and overlaps.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreateAccountingPeriodInput & CF,
  _ctx: CommandContext,
) {
  // Validate fiscal year exists and belongs to the same company
  const fiscalYear = await db
    .selectFrom("FiscalYear")
    .selectAll()
    .where("id", "=", input.fiscalYearId)
    .executeTakeFirst();

  if (!fiscalYear) {
    return err(new FiscalYearNotFoundError(input.fiscalYearId));
  }
  if (fiscalYear.companyId !== input.companyId) {
    return err(new FiscalYearNotFoundError(input.fiscalYearId));
  }

  // Validate name is non-empty
  if (!input.name || input.name.trim() === "") {
    return err(new NameRequiredError(String(input.name)));
  }

  // Validate start date is strictly before end date
  const startTime = new Date(input.startDate).getTime();
  const endTime = new Date(input.endDate).getTime();
  if (startTime >= endTime) {
    return err(new InvalidDateRangeError(`${String(input.startDate)} - ${String(input.endDate)}`));
  }

  // Validate period falls within fiscal year date range (local-time date-only comparison)
  const toDateNum = (d: Date | string) => {
    const dt = new Date(d);
    return dt.getFullYear() * 10000 + (dt.getMonth() + 1) * 100 + dt.getDate();
  };
  const periodStartNum = toDateNum(input.startDate);
  const periodEndNum = toDateNum(input.endDate);
  const fyStartNum = toDateNum(fiscalYear.startDate);
  const fyEndNum = toDateNum(fiscalYear.endDate);
  if (periodStartNum < fyStartNum || periodEndNum > fyEndNum) {
    return err(
      new InvalidDateRangeError(
        `Period dates must fall within fiscal year range: ${String(input.startDate)} - ${String(input.endDate)}`,
      ),
    );
  }

  // Fetch existing periods in the same fiscal year
  const existingPeriods = await db
    .selectFrom("AccountingPeriod")
    .selectAll()
    .where("fiscalYearId", "=", input.fiscalYearId)
    .execute();

  // Check for duplicate dates (identical start and end, neither is adjustment)
  for (const existing of existingPeriods) {
    const existingStart = new Date(existing.startDate).getTime();
    const existingEnd = new Date(existing.endDate).getTime();
    if (existingStart === startTime && existingEnd === endTime) {
      if (existing.periodType !== "ADJUSTMENT" && input.periodType !== "ADJUSTMENT") {
        return err(new DuplicatePeriodDatesError(input.name));
      }
      // Adjustment period sharing dates with a regular period is allowed
      continue;
    }
  }

  // Check for overlapping periods (excluding adjustment periods that share end dates)
  for (const existing of existingPeriods) {
    const existingStart = new Date(existing.startDate).getTime();
    const existingEnd = new Date(existing.endDate).getTime();

    // Skip overlap check when one is an adjustment period sharing the same end date
    if (
      (input.periodType === "ADJUSTMENT" || existing.periodType === "ADJUSTMENT") &&
      existingEnd === endTime
    ) {
      continue;
    }

    // Standard overlap check: two ranges overlap if startA < endB and startB < endA
    if (startTime < existingEnd && existingStart < endTime) {
      return err(new OverlappingPeriodError(input.name));
    }
  }

  // Create accounting period in NEVER_OPENED status
  const { companyId, fiscalYearId, name, startDate, endDate, periodType, ...customFields } = input;
  const accountingPeriod = await db
    .insertInto("AccountingPeriod")
    .values({
      ...(customFields as Record<string, unknown>),
      companyId,
      fiscalYearId,
      name,
      startDate,
      endDate,
      periodType,
      status: "NEVER_OPENED",
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ accountingPeriod });
}
