import { err, ok, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction } from "../generated/kysely-tailordb";
import { isPositiveDecimal } from "../internal/transferOrderProgress";
import {
  EmptyTransferLinesError,
  InvalidQuantityError,
  InvalidTransferLocationsError,
  ItemNotFoundError,
  SiteNotFoundError,
} from "../lib/errors.generated";
import type { ItemManagementQueries, OrganizationQueries } from "../module";

export interface TransferOrderLineInput {
  itemId: string;
  orderedQuantity: string;
  lotId?: string | null;
  serialNumberId?: string | null;
}

export interface TransferOrderHeaderInput {
  sourceSiteId: string;
  destinationSiteId: string;
  plannedShipmentDate: Date;
  expectedReceiptDate: Date;
}

export interface CreateTransferOrderInput {
  header: TransferOrderHeaderInput;
  lines: TransferOrderLineInput[];
}

export async function run<CF extends object = object, LCF extends object = object>(
  db: Transaction,
  input: {
    header: TransferOrderHeaderInput & Partial<CF>;
    lines: (TransferOrderLineInput & Partial<LCF>)[];
  },
  ctx: CommandContext,
  itemManagementQueries: Pick<ItemManagementQueries, "getItem">,
  organizationQueries: Pick<OrganizationQueries, "getSite">,
) {
  const { header, lines } = input;
  const { sourceSiteId, destinationSiteId, plannedShipmentDate, expectedReceiptDate } = header;
  if (sourceSiteId === destinationSiteId) {
    return err(new InvalidTransferLocationsError(sourceSiteId));
  }
  if (lines.length === 0) {
    return err(new EmptyTransferLinesError(sourceSiteId));
  }

  for (const siteId of [sourceSiteId, destinationSiteId]) {
    const { site } = (await organizationQueries.getSite(db, { id: siteId }, ctx)).value;
    if (!site) {
      return err(new SiteNotFoundError(siteId));
    }
  }

  const validatedLines: { line: TransferOrderLineInput & Partial<LCF>; unitId: string }[] = [];
  for (const line of lines) {
    if (!isPositiveDecimal(line.orderedQuantity)) {
      return err(new InvalidQuantityError(line.orderedQuantity));
    }
    const { item } = (await itemManagementQueries.getItem(db, { id: line.itemId }, ctx)).value;
    if (!item) {
      return err(new ItemNotFoundError(line.itemId));
    }
    validatedLines.push({ line, unitId: item.unitId });
  }

  const transferOrder = await db
    .insertInto("TransferOrder")
    .values({
      // Header input first (incl. custom fields) so command-set columns always win.
      ...(header as Record<string, unknown>),
      sourceSiteId,
      destinationSiteId,
      plannedShipmentDate,
      expectedReceiptDate,
      status: "DRAFT",
      closedAt: null,
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  await db
    .insertInto("TransferOrderLine")
    .values(
      validatedLines.map(({ line, unitId }) => {
        const { itemId, orderedQuantity, lotId, serialNumberId, ...lineCustomFields } = line;
        return {
          ...lineCustomFields,
          transferOrderId: transferOrder.id,
          itemId,
          unitId,
          orderedQuantity,
          shippedQuantity: "0",
          receivedQuantity: "0",
          lotId: lotId ?? null,
          serialNumberId: serialNumberId ?? null,
        };
      }),
    )
    .execute();

  return ok({ transferOrder });
}
