import type { Transaction } from "../generated/kysely-tailordb";
import {
  OpenChangeOrderExistsError,
  PurchaseOrderAlreadyFulfilledError,
  PurchaseOrderNotCancellableError,
  PurchaseOrderNotFoundError,
} from "../lib/errors.generated";
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";

export interface CancelPurchaseOrderInput {
  id: string;
}

export async function run(db: Transaction, input: CancelPurchaseOrderInput, 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 (!["DRAFT", "SUBMITTED", "ORDERED"].includes(purchaseOrder.orderStatus as string)) {
    return err(new PurchaseOrderNotCancellableError(input.id));
  }

  // Block cancellation if an open change order exists
  const openCO = await db
    .selectFrom("PurchaseChangeOrder")
    .selectAll()
    .where("purchaseOrderId", "=", input.id)
    .where("changeOrderStatus", "in", ["DRAFT", "SUBMITTED", "APPROVED"])
    .executeTakeFirst();
  if (openCO) {
    return err(new OpenChangeOrderExistsError(input.id));
  }

  const purchaseOrderLines = await db
    .selectFrom("PurchaseOrderLine")
    .selectAll()
    .where("purchaseOrderId", "=", input.id)
    .execute();
  const purchaseOrderLineIds = new Set(purchaseOrderLines.map((line) => line.id));

  // Check for posted receipts via receipt lines referencing this PO's lines
  if (purchaseOrderLineIds.size > 0) {
    const postedReceiptLine = await db
      .selectFrom("GoodsReceiptLine")
      .innerJoin("GoodsReceipt", "GoodsReceipt.id", "GoodsReceiptLine.goodsReceiptId")
      .selectAll("GoodsReceiptLine")
      .where("GoodsReceiptLine.purchaseOrderLineId", "in", [...purchaseOrderLineIds])
      .where("GoodsReceipt.status", "=", "POSTED")
      .executeTakeFirst();
    if (postedReceiptLine) {
      return err(new PurchaseOrderAlreadyFulfilledError(input.id));
    }
  }

  const matchedBills = await db
    .selectFrom("PurchaseBill")
    .selectAll()
    .where("status", "=", "MATCHED")
    .execute();
  for (const bill of matchedBills) {
    const billLines = await db
      .selectFrom("PurchaseBillLine")
      .selectAll()
      .where("purchaseBillId", "=", bill.id)
      .execute();
    if (
      billLines.some(
        (line) =>
          line.purchaseOrderLineId !== null && purchaseOrderLineIds.has(line.purchaseOrderLineId),
      )
    ) {
      return err(new PurchaseOrderAlreadyFulfilledError(input.id));
    }
  }

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

  return ok({ purchaseOrder: cancelled });
}
