/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
import type { Transaction } from "../generated/kysely-tailordb";
import {
  GoodsReceiptNotDraftError,
  GoodsReceiptNotFoundError,
  InvalidReceivedQuantityError,
  NoOpenReceiptQuantityError,
  PurchaseOrderLineNotFoundError,
} 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 type UpdateGoodsReceiptInput = {
  id: string;
  receiptDate?: Date;
  lines?: GoodsReceiptLineInput[];
};
export async function run(db: Transaction, input: UpdateGoodsReceiptInput, ctx: CommandContext) {
  void ctx;
  const goodsReceipt = await selectOne(db, "GoodsReceipt", "id", input.id, true);
  if (!goodsReceipt) {
    return err(new GoodsReceiptNotFoundError(input.id));
  }
  if (goodsReceipt.status !== "DRAFT") {
    return err(new GoodsReceiptNotDraftError(input.id));
  }

  const purchaseOrder = await selectOne(
    db,
    "PurchaseOrder",
    "id",
    goodsReceipt.purchaseOrderId as string,
  );
  const orderLines = decodeJson<PurchaseOrderLineInput[]>(purchaseOrder?.lines, []);
  const lines = normalizeReceiptLines(
    input.lines ?? decodeJson<GoodsReceiptLineInput[]>(goodsReceipt.lines, []),
  );

  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 updated = await db
    .updateTable("GoodsReceipt")
    .set({
      receiptDate: input.receiptDate ?? goodsReceipt.receiptDate,
      lines: encodeJson(lines),
      updatedAt: new Date(),
    })
    .where("id", "=", input.id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ goodsReceipt: updated });
}
