import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";
import type { Transaction } 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));
  }

  const yearName = input.yearName ?? existing.name;
  if (!yearName || 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 ?? existing.startDate;
    const newEndDate = 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 fiscalYear = await db
    .updateTable("FiscalYear")
    .set({
      name: yearName,
      ...(input.startDate !== undefined ? { startDate: input.startDate } : {}),
      ...(input.endDate !== undefined ? { endDate: input.endDate } : {}),
      updatedAt: new Date(),
    })
    .where("id", "=", input.id)
    .returningAll()
    .executeTakeFirstOrThrow();

  // Emit audit event recording the acting user, timestamp, and changed fields
  void ctx;

  return ok({ fiscalYear });
}
