import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import { Decimal } from "decimal.js";
import type { Transaction } from "../generated/kysely-tailordb";
import { deriveReceiptStatus, type LineProgress } from "../lib/domain";
import { PurchaseOrderNotFoundError } from "../lib/errors.generated";
import type { InventoryQueries } from "../module";

export interface RecalculatePurchaseOrderReceiptStatusInternalInput {
  purchaseOrderId: string;
}

export async function recalculatePurchaseOrderReceiptStatusInternal(
  db: Transaction,
  input: RecalculatePurchaseOrderReceiptStatusInternalInput,
  ctx: CommandContext,
  inventoryQueries: Pick<InventoryQueries, "listPurchaseReceiptEvidenceForMatching">,
) {
  const purchaseOrder = await db
    .selectFrom("PurchaseOrder")
    .selectAll()
    .where("id", "=", input.purchaseOrderId)
    .forUpdate()
    .executeTakeFirst();
  if (!purchaseOrder) {
    return err(new PurchaseOrderNotFoundError(input.purchaseOrderId));
  }

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

  const receivedByLine = new Map<string, Decimal>();
  const lineIds = lines.map((line) => line.id);
  if (lineIds.length > 0) {
    const receiptEvidence = await inventoryQueries.listPurchaseReceiptEvidenceForMatching(
      db,
      { purchaseOrderId: input.purchaseOrderId, purchaseOrderLineIds: lineIds },
      ctx,
    );
    for (const evidence of receiptEvidence.value.receiptEvidence) {
      receivedByLine.set(
        evidence.purchaseOrderLineId,
        (receivedByLine.get(evidence.purchaseOrderLineId) ?? new Decimal(0)).plus(
          evidence.quantity,
        ),
      );
    }
  }

  const lineProgress: LineProgress[] = lines.map((line) => ({
    quantity: line.quantity,
    receivedQuantity: (receivedByLine.get(line.id) ?? new Decimal(0)).toString(),
  }));

  const updated = await db
    .updateTable("PurchaseOrder")
    .set({ receiptStatus: deriveReceiptStatus(lineProgress), updatedAt: new Date() })
    .where("id", "=", input.purchaseOrderId)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ purchaseOrder: updated });
}
