import type { defineModule as defineBusinessPartnerModule } from "../../business-partner";
import type { defineModule as defineItemManagementModule } from "../../item-management";
import type { Transaction } from "../generated/kysely-tailordb";
type BusinessPartnerModule = ReturnType<typeof defineBusinessPartnerModule>;
type BusinessPartnerQueries = BusinessPartnerModule["queries"];
type ItemManagementModule = ReturnType<typeof defineItemManagementModule>;
type ItemManagementQueries = ItemManagementModule["queries"];
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import { purchaseOrderLifecycle } from "../db/purchaseOrder.lifecycle.generated";
import {
  ItemNotActiveError,
  PartnerNotSupplierError,
  PurchaseOrderNotFoundError,
  PurchaseOrderNotSubmittedError,
  SupplierNotActiveError,
} from "../lib/errors.generated";

export interface ApprovePurchaseOrderInput {
  id: string;
}

export async function run(
  db: Transaction,
  input: ApprovePurchaseOrderInput,
  ctx: CommandContext,
  businessPartnerQueries: Pick<BusinessPartnerQueries, "getPartner" | "getPartnerRole">,
  itemManagementQueries: Pick<ItemManagementQueries, "getItem">,
) {
  const purchaseOrder = await db
    .selectFrom("PurchaseOrder")
    .selectAll()
    .where("id", "=", input.id)
    .forUpdate()
    .executeTakeFirst();
  if (!purchaseOrder) {
    return err(new PurchaseOrderNotFoundError(input.id));
  }
  const nextOrderStatus = purchaseOrderLifecycle.tryTransition(
    purchaseOrder.orderStatus,
    "approve",
  );
  if (!nextOrderStatus) {
    return err(new PurchaseOrderNotSubmittedError(input.id));
  }

  const { partner: supplier } = (
    await businessPartnerQueries.getPartner(db, { id: purchaseOrder.supplierId }, ctx)
  ).value;
  if (supplier?.status !== "ACTIVE") {
    return err(new SupplierNotActiveError(purchaseOrder.supplierId));
  }
  const { partnerRole } = (
    await businessPartnerQueries.getPartnerRole(
      db,
      { partnerId: purchaseOrder.supplierId, role: "SUPPLIER" },
      ctx,
    )
  ).value;
  if (!partnerRole) {
    return err(new PartnerNotSupplierError(purchaseOrder.supplierId));
  }

  const lines = await db
    .selectFrom("PurchaseOrderLine")
    .selectAll()
    .where("purchaseOrderId", "=", input.id)
    .execute();

  for (const line of lines) {
    const { item } = (await itemManagementQueries.getItem(db, { id: line.itemId }, ctx)).value;
    if (item?.status !== "ACTIVE") {
      return err(new ItemNotActiveError(line.itemId));
    }
  }

  const approved = await db
    .updateTable("PurchaseOrder")
    .set({
      orderStatus: nextOrderStatus,
      receiptStatus: "NOT_RECEIVED",
      billingStatus: "NOT_BILLED",
      revisionNumber: 1,
      updatedAt: new Date(),
    })
    .where("id", "=", input.id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ purchaseOrder: approved });
}
