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 type UpdateFiscalYearInput = {
  id: string;
  yearName?: string;
  startDate?: Date;
  endDate?: Date;
};
export async function run(db: Transaction, input: UpdateFiscalYearInput, _ctx: CommandContext) {
  const existing = await db
    .selectFrom("FiscalYear")
    .selectAll()
    .where("id", "=", input.id)
    .forUpdate()
    .executeTakeFirst();

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

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

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

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

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

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

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

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

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

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

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

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

  return ok({ fiscalYear });
}
