/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
import type { Transaction } from "../generated/kysely-tailordb";
import {
  EmptyPurchaseOrderLinesError,
  ItemNotActiveError,
  ItemNotFoundError,
  InvalidQuantityError,
  InvalidUnitPriceError,
  PartnerNotSupplierError,
  PaymentTermInactiveError,
  ReceivingSiteNotFoundError,
  ReceivingSiteRequiredError,
  SupplierNotActiveError,
  SupplierNotFoundError,
} from "../lib/errors.generated";
import {
  buildPaymentTermSnapshot,
  encodeJson,
  getSupplierProfileByCompanyAndSupplier,
  getSupplierRole,
  isActiveStatus,
  normalizeOrderLines,
  selectOne,
  type PurchaseOrderLineInput,
} from "../lib/domain";
import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface CreatePurchaseOrderInput {
  companyId: string;
  supplierId: string;
  orderDate?: Date;
  documentReceivingSiteId?: string | null;
  paymentTermId?: string | null;
  externalSupplierOrderReference?: string;
  lines: PurchaseOrderLineInput[];
}

export async function run(db: Transaction, input: CreatePurchaseOrderInput, ctx: CommandContext) {
  void ctx;
  const supplier = await selectOne(db, "BusinessPartner", "id", input.supplierId);
  if (!supplier) {
    return err(new SupplierNotFoundError(input.supplierId));
  }
  if (!isActiveStatus(supplier.status as string | null)) {
    return err(new SupplierNotActiveError(input.supplierId));
  }
  if (!(await getSupplierRole(db, input.supplierId))) {
    return err(new PartnerNotSupplierError(input.supplierId));
  }
  if (input.lines.length === 0) {
    return err(new EmptyPurchaseOrderLinesError(input.companyId));
  }

  let selectedPaymentTermId = input.paymentTermId ?? null;
  if (!selectedPaymentTermId) {
    const supplierProfile = await getSupplierProfileByCompanyAndSupplier(
      db,
      input.companyId,
      input.supplierId,
    );
    selectedPaymentTermId =
      (supplierProfile?.defaultPurchasePaymentTermId as string | null) ?? null;
  }

  let paymentTermSnapshot: string | null = null;
  if (selectedPaymentTermId) {
    const paymentTerm = await selectOne(db, "PurchasePaymentTerm", "id", selectedPaymentTermId);
    if (paymentTerm && !isActiveStatus(paymentTerm.status as string | null)) {
      return err(new PaymentTermInactiveError(selectedPaymentTermId));
    }
    if (paymentTerm) {
      paymentTermSnapshot = encodeJson(buildPaymentTermSnapshot(paymentTerm));
    }
  }

  for (const line of input.lines) {
    const item = await selectOne(db, "Item", "id", line.itemId);
    if (!item) {
      return err(new ItemNotFoundError(line.itemId));
    }
    if (!isActiveStatus(item.status as string | null)) {
      return err(new ItemNotActiveError(line.itemId));
    }
    if (line.quantity <= 0) {
      return err(new InvalidQuantityError(line.itemId));
    }
    if (line.unitPrice < 0) {
      return err(new InvalidUnitPriceError(line.itemId));
    }

    const receivingSiteId = line.receivingSiteId ?? input.documentReceivingSiteId ?? null;
    if ((line.requiresPhysicalReceipt ?? true) && !receivingSiteId) {
      return err(new ReceivingSiteRequiredError(line.itemId));
    }
    if (receivingSiteId) {
      const site = await selectOne(db, "Site", "id", receivingSiteId);
      if (!site) {
        return err(new ReceivingSiteNotFoundError(receivingSiteId));
      }
    }
  }

  const lines = normalizeOrderLines(
    input.lines.map((line) => ({
      ...line,
      receivingSiteId: line.receivingSiteId ?? input.documentReceivingSiteId ?? null,
    })),
  );

  const purchaseOrder = await db
    .insertInto("PurchaseOrder")
    .values({
      companyId: input.companyId,
      supplierId: input.supplierId,
      documentReceivingSiteId: input.documentReceivingSiteId ?? null,
      paymentTermId: selectedPaymentTermId,
      status: "DRAFT",
      orderDate: input.orderDate ?? new Date(),
      externalSupplierOrderReference: input.externalSupplierOrderReference ?? null,
      lines: encodeJson(lines),
      paymentTermSnapshot,
      rejectionReason: null,
      closeReason: null,
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

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