/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
import type { Transaction } from "../generated/kysely-tailordb";
import {
  CompanyScopeMismatchError,
  EmptyReceiptLinesError,
  InvalidReceivedQuantityError,
  NoOpenReceiptQuantityError,
  PurchaseOrderLineNotFoundError,
  PurchaseOrderNotFoundError,
  PurchaseOrderNotReceivableError,
} from "../lib/errors.generated";
import {
  decodeJson,
  encodeJson,
  normalizeReceiptLines,
  selectOne,
  type GoodsReceiptLineInput,
  type PurchaseOrderLineInput,
} from "../lib/domain";
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface CreateGoodsReceiptInput {
  companyId: string;
  supplierId: string;
  purchaseOrderId: string;
  receiptDate?: Date;
  lines: GoodsReceiptLineInput[];
}

export async function run(db: Transaction, input: CreateGoodsReceiptInput, ctx: CommandContext) {
  void ctx;
  const purchaseOrder = await selectOne(db, "PurchaseOrder", "id", input.purchaseOrderId);
  if (!purchaseOrder) {
    return err(new PurchaseOrderNotFoundError(input.purchaseOrderId));
  }
  if (purchaseOrder.companyId !== input.companyId) {
    return err(new CompanyScopeMismatchError(input.purchaseOrderId));
  }
  if (["CANCELLED", "CLOSED"].includes(purchaseOrder.status as string)) {
    return err(new PurchaseOrderNotReceivableError(input.purchaseOrderId));
  }
  if (input.lines.length === 0) {
    return err(new EmptyReceiptLinesError(input.purchaseOrderId));
  }

  const orderLines = decodeJson<PurchaseOrderLineInput[]>(purchaseOrder.lines, []);
  const lines: ReturnType<typeof normalizeReceiptLines> = [];
  for (const line of normalizeReceiptLines(input.lines)) {
    const orderLine = orderLines.find((candidate) => candidate.id === line.purchaseOrderLineId);
    if (!orderLine) {
      return err(new PurchaseOrderLineNotFoundError(line.purchaseOrderLineId));
    }
    lines.push({
      ...line,
      itemId: line.itemId ?? orderLine.itemId,
      unitId: line.unitId ?? orderLine.unitId ?? null,
      destinationSite: line.destinationSite ?? orderLine.receivingSiteId ?? null,
      requiresInventoryHandoff: line.requiresInventoryHandoff ?? orderLine.requiresPhysicalReceipt,
    });
  }

  for (const line of lines) {
    const orderLine = orderLines.find((candidate) => candidate.id === line.purchaseOrderLineId);
    if (!orderLine) {
      return err(new PurchaseOrderLineNotFoundError(line.purchaseOrderLineId));
    }
    if (line.quantity <= 0) {
      return err(new InvalidReceivedQuantityError(line.purchaseOrderLineId));
    }
    const openQuantity = orderLine.quantity - (orderLine.receivedQuantity ?? 0);
    if (openQuantity <= 0) {
      return err(new NoOpenReceiptQuantityError(line.purchaseOrderLineId));
    }
  }

  const goodsReceipt = await db
    .insertInto("GoodsReceipt")
    .values({
      companyId: input.companyId,
      supplierId: input.supplierId,
      purchaseOrderId: input.purchaseOrderId,
      receiptDate: input.receiptDate ?? new Date(),
      status: "DRAFT",
      lines: encodeJson(lines),
      inventoryHandoff: null,
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  return ok({ goodsReceipt: goodsReceipt! });
}
