import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Selectable, Transaction, Updateable } from "../generated/kysely-tailordb";
import { validateScheduleLines } from "../lib/domain";
import {
  EmptyScheduleError,
  ImmutableCompanyScopeError,
  InvalidInstallmentTotalError,
  InvalidScheduleLineError,
  PaymentTermNotFoundError,
} from "../lib/errors.generated";

export type UpdatePurchasePaymentTermInput = {
  id: string;
  companyId?: string;
  name?: string;
  code?: string | null;
  scheduleLines?: Pick<
    Selectable<"PurchasePaymentTermLine">,
    "kind" | "days" | "fixedDayOfMonth" | "percentage" | "fixedAmount" | "note"
  >[];
  notes?: string | null;
};
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdatePurchasePaymentTermInput & Partial<CF>,
  _ctx: CommandContext,
) {
  const {
    id,
    companyId: inputCompanyId,
    name,
    code,
    scheduleLines: inputScheduleLines,
    notes,
    ...customFields
  } = input;
  const existing = await db
    .selectFrom("PurchasePaymentTerm")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();
  if (!existing) {
    return err(new PaymentTermNotFoundError(id));
  }

  if (inputCompanyId && inputCompanyId !== existing.companyId) {
    return err(new ImmutableCompanyScopeError(id));
  }

  let scheduleLines: Pick<
    Selectable<"PurchasePaymentTermLine">,
    "kind" | "days" | "fixedDayOfMonth" | "percentage" | "fixedAmount" | "note"
  >[];
  if (inputScheduleLines) {
    scheduleLines = inputScheduleLines;
  } else {
    const existingLines = await db
      .selectFrom("PurchasePaymentTermLine")
      .selectAll()
      .where("purchasePaymentTermId", "=", id)
      .execute();
    scheduleLines = existingLines;
  }

  const validation = validateScheduleLines(scheduleLines);
  if (validation === "EMPTY_SCHEDULE") {
    return err(new EmptyScheduleError(id));
  }
  if (validation === "INVALID_INSTALLMENT_TOTAL") {
    return err(new InvalidInstallmentTotalError(id));
  }
  if (validation === "INVALID_SCHEDULE_LINE") {
    return err(new InvalidScheduleLineError(id));
  }

  const updates: Updateable<"PurchasePaymentTerm"> = {
    ...(customFields as Updateable<"PurchasePaymentTerm">),
  };
  if (name !== undefined) updates.name = name;
  if (code !== undefined) updates.code = code;
  if (notes !== undefined) updates.notes = notes;

  const paymentTerm =
    Object.keys(updates).length === 0
      ? existing
      : await db
          .updateTable("PurchasePaymentTerm")
          .set(updates)
          .where("id", "=", id)
          .returningAll()
          .executeTakeFirstOrThrow();

  if (inputScheduleLines) {
    await db
      .deleteFrom("PurchasePaymentTermLine")
      .where("purchasePaymentTermId", "=", id)
      .execute();

    await db
      .insertInto("PurchasePaymentTermLine")
      .values(
        inputScheduleLines.map((line) => ({
          purchasePaymentTermId: id,
          kind: line.kind,
          days: line.days,
          fixedDayOfMonth: line.fixedDayOfMonth,
          percentage: line.percentage,
          fixedAmount: line.fixedAmount,
          note: line.note,
        })),
      )
      .execute();
  }

  return ok({ purchasePaymentTerm: paymentTerm });
}
