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

export interface UpdateFiscalYearInput {
  id: string;
  yearName?: string;
  startDate?: Date;
  endDate?: Date;
}

/**
 * Function: updateFiscalYear
 *
 * Updates fiscal year name and, before periods exist, fiscal year date bounds.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateFiscalYearInput & Partial<CF>,
  _ctx: CommandContext,
) {
  const { id, yearName, startDate, endDate, ...customFields } = input;

  const existing = await db
    .selectFrom("FiscalYear")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();

  if (!existing) {
    return err(new FiscalYearNotFoundError(id));
  }

  if (yearName !== undefined && yearName.trim() === "") {
    return err(new NameRequiredError(id));
  }

  const datesRequested = startDate !== undefined || endDate !== undefined;

  if (datesRequested) {
    const periodsExist = await db
      .selectFrom("AccountingPeriod")
      .selectAll()
      .where("fiscalYearId", "=", id)
      .executeTakeFirst();

    if (periodsExist) {
      return err(new DatesImmutableError(id));
    }

    const newStartDate = startDate !== undefined ? startDate : existing.startDate;
    const newEndDate = endDate !== undefined ? endDate : existing.endDate;

    if (newStartDate >= newEndDate) {
      return err(new InvalidDateRangeError(id));
    }

    const overlapping = await db
      .selectFrom("FiscalYear")
      .selectAll()
      .where("companyId", "=", existing.companyId)
      .where("id", "!=", id)
      .where("startDate", "<", newEndDate)
      .where("endDate", ">", newStartDate)
      .executeTakeFirst();

    if (overlapping) {
      return err(new OverlappingFiscalYearError(id));
    }
  }

  const updateSet: Updateable<"FiscalYear"> = { ...(customFields as Updateable<"FiscalYear">) };
  if (yearName !== undefined) updateSet.name = yearName;
  if (startDate !== undefined) updateSet.startDate = startDate;
  if (endDate !== undefined) updateSet.endDate = endDate;

  if (Object.keys(updateSet).length === 0) {
    return ok({ fiscalYear: existing });
  }

  const fiscalYear = await db
    .updateTable("FiscalYear")
    .set(updateSet)
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ fiscalYear });
}
