import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  ProductionItemNotFoundError,
  ProductionItemNotManufacturableError,
  InvalidPlannedQuantityError,
  InvalidSiteScopeError,
  CrossScopeMasterReferenceError,
} from "../lib/errors.generated";
import type { ItemManagementQueries, OrganizationQueries } from "../module";

export interface CreateProductionOrderInput {
  orderedItemId: string;
  companyId: string;
  siteId: string;
  plannedQuantity: number;
  plannedStartDate: Date;
  plannedEndDate: Date;
  selectedBomVersionId?: string | null;
  selectedRoutingRevisionId?: string | null;
}

/**
 * Function: createProductionOrder
 *
 * Creates a draft manufacturing request with produced item, quantity, site,
 * and planned dates. Records planner intent without freezing BOM, routing,
 * work orders, or cost assumptions.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreateProductionOrderInput & CF,
  ctx: CommandContext,
  itemManagementQueries: Pick<ItemManagementQueries, "getItem">,
  organizationQueries: Pick<OrganizationQueries, "getSite">,
) {
  const {
    orderedItemId,
    companyId,
    siteId,
    plannedQuantity,
    plannedStartDate,
    plannedEndDate,
    selectedBomVersionId,
    selectedRoutingRevisionId,
    ...customFields
  } = input;

  // 1. Validate planned quantity > 0
  if (plannedQuantity <= 0) {
    return err(new InvalidPlannedQuantityError(orderedItemId));
  }

  // 2. Validate scope
  if (!companyId || !siteId) {
    return err(new InvalidSiteScopeError(orderedItemId));
  }

  // 3. Validate produced item exists
  const { item } = (await itemManagementQueries.getItem(db, { id: orderedItemId }, ctx)).value;

  if (!item) {
    return err(new ProductionItemNotFoundError(orderedItemId));
  }

  // 4. Validate item is manufacturable
  if (item && "isManufacturable" in item && item.isManufacturable === false) {
    return err(new ProductionItemNotManufacturableError(orderedItemId));
  }

  // 5. Validate site belongs to company scope
  const { site } = (await organizationQueries.getSite(db, { id: siteId }, ctx)).value;

  if (!site) {
    return err(new InvalidSiteScopeError(orderedItemId));
  }

  // 5. Validate optional BOM reference scope
  if (selectedBomVersionId) {
    const bom = await db
      .selectFrom("BillOfMaterial")
      .selectAll()
      .where("id", "=", selectedBomVersionId)
      .executeTakeFirst();

    if (bom && (bom.companyId !== companyId || (bom.siteId != null && bom.siteId !== siteId))) {
      return err(new CrossScopeMasterReferenceError(orderedItemId));
    }
  }

  // 6. Validate optional routing reference scope
  if (selectedRoutingRevisionId) {
    const routing = await db
      .selectFrom("Routing")
      .selectAll()
      .where("id", "=", selectedRoutingRevisionId)
      .executeTakeFirst();

    if (
      routing &&
      (routing.companyId !== companyId || (routing.siteId != null && routing.siteId !== siteId))
    ) {
      return err(new CrossScopeMasterReferenceError(orderedItemId));
    }
  }

  // 7. Create draft production order
  const productionOrder = await db
    .insertInto("ProductionOrder")
    .values({
      ...(customFields as Record<string, unknown>),
      orderedItemId,
      companyId,
      siteId,
      plannedQuantity,
      plannedStartDate,
      plannedEndDate,
      selectedBomVersionId: selectedBomVersionId ?? null,
      selectedRoutingRevisionId: selectedRoutingRevisionId ?? null,
      status: "DRAFT",
    })
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ productionOrder });
}
