import type { DB, Selectable } from "../generated/kysely-tailordb";
import {
  InvalidBillAmountError,
  InvalidPaymentTermConfigurationError,
  PaymentTermNotFoundError,
} from "../lib/errors.generated";
import { calculateDueSchedule } from "../lib/domain";
import { err, ok, type ReadonlyDB, type QueryContext } from "@tailor-platform/erp-kit/core";

type SnapshotLine = NonNullable<Selectable<"PurchaseOrder">["paymentTermSnapshotLines"]>[number];

export type CalculatePurchaseBillDueScheduleInput =
  | {
      purchasePaymentTermId: string;
      billDate: Date;
      billAmount: string;
    }
  | {
      paymentTermSnapshotLines: SnapshotLine[];
      billDate: Date;
      billAmount: string;
    };

export async function run(
  db: ReadonlyDB<DB>,
  input: CalculatePurchaseBillDueScheduleInput,
  ctx?: QueryContext,
) {
  void ctx;

  let scheduleLines: SnapshotLine[];
  let paymentTerm: Selectable<"PurchasePaymentTerm"> | null = null;

  if ("paymentTermSnapshotLines" in input) {
    scheduleLines = input.paymentTermSnapshotLines;
  } else {
    paymentTerm =
      (await db
        .selectFrom("PurchasePaymentTerm")
        .selectAll()
        .where("id", "=", input.purchasePaymentTermId)
        .executeTakeFirst()) ?? null;
    if (!paymentTerm) {
      return err(new PaymentTermNotFoundError(input.purchasePaymentTermId));
    }

    scheduleLines = await db
      .selectFrom("PurchasePaymentTermLine")
      .selectAll()
      .where("purchasePaymentTermId", "=", input.purchasePaymentTermId)
      .execute();
  }

  const result = calculateDueSchedule(scheduleLines, input.billDate, input.billAmount);
  if ("error" in result) {
    if (result.error === "INVALID_BILL_AMOUNT") {
      return err(new InvalidBillAmountError(input.billAmount));
    }
    const identifier = "purchasePaymentTermId" in input ? input.purchasePaymentTermId : "snapshot";
    return err(new InvalidPaymentTermConfigurationError(identifier));
  }

  return ok({ dueSchedule: result.schedule, paymentTerm });
}
