/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
import type { Transaction } from "../generated/kysely-tailordb";
import {
  DuplicateExternalSupplierOrderReferenceError,
  EmptyPurchaseOrderLinesError,
  PartnerNotSupplierError,
  PurchaseOrderNotDraftError,
  PurchaseOrderNotFoundError,
  ReceivingSiteRequiredError,
} from "../lib/errors.generated";
import {
  decodeJson,
  findPurchaseOrderByExternalReference,
  getSupplierRole,
  type PurchaseOrderLineInput,
} from "../lib/domain";
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface SubmitPurchaseOrderInput {
  id: string;
}

export async function run(db: Transaction, input: SubmitPurchaseOrderInput, ctx: CommandContext) {
  void ctx;
  const purchaseOrder = await db
    .selectFrom("PurchaseOrder")
    .selectAll()
    .where("id", "=", input.id)
    .forUpdate()
    .executeTakeFirst();
  if (!purchaseOrder) {
    return err(new PurchaseOrderNotFoundError(input.id));
  }
  if (purchaseOrder.status !== "DRAFT") {
    return err(new PurchaseOrderNotDraftError(input.id));
  }

  const lines = decodeJson<PurchaseOrderLineInput[]>(purchaseOrder.lines, []);
  if (lines.length === 0) {
    return err(new EmptyPurchaseOrderLinesError(input.id));
  }
  for (const line of lines) {
    if ((line.requiresPhysicalReceipt ?? true) && !line.receivingSiteId) {
      return err(new ReceivingSiteRequiredError(line.itemId));
    }
  }
  if (!(await getSupplierRole(db, purchaseOrder.supplierId))) {
    return err(new PartnerNotSupplierError(purchaseOrder.supplierId));
  }
  if (purchaseOrder.externalSupplierOrderReference) {
    const duplicate = await findPurchaseOrderByExternalReference(
      db,
      purchaseOrder.companyId,
      purchaseOrder.externalSupplierOrderReference,
    );
    if (duplicate && duplicate.id !== input.id) {
      return err(
        new DuplicateExternalSupplierOrderReferenceError(
          purchaseOrder.externalSupplierOrderReference,
        ),
      );
    }
  }

  const submitted = await db
    .updateTable("PurchaseOrder")
    .set({ status: "SUBMITTED", updatedAt: new Date() })
    .where("id", "=", input.id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ purchaseOrder: submitted });
}
