import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction, Updateable } from "../generated/kysely-tailordb";
import {
  ProductionOrderNotFoundError,
  ProductionOrderNotMutableError,
  InvalidPlannedQuantityError,
  CrossScopeMasterReferenceError,
} from "../lib/errors.generated";

export type UpdateProductionOrderInput = {
  id: string;
  plannedQuantity?: number;
  plannedStartDate?: Date;
  plannedEndDate?: Date;
  selectedBomVersionId?: string | null;
  selectedRoutingRevisionId?: string | null;
};
/**
 * Function: updateProductionOrder
 *
 * Revises mutable draft planning fields such as dates, quantity, and
 * preferred BOM or routing before release freezes execution assumptions.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: UpdateProductionOrderInput & Omit<Partial<CF>, "status">,
  _ctx: CommandContext,
) {
  const {
    id,
    plannedQuantity,
    plannedStartDate,
    plannedEndDate,
    selectedBomVersionId,
    selectedRoutingRevisionId,
    ...customFields
  } = input;

  // 1. Fetch production order with lock
  const order = await db
    .selectFrom("ProductionOrder")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();

  if (!order) {
    return err(new ProductionOrderNotFoundError(id));
  }

  // 2. Only DRAFT orders are mutable
  if (order.status !== "DRAFT") {
    return err(new ProductionOrderNotMutableError(id));
  }

  // 3. Validate planned quantity if provided
  if (plannedQuantity != null && plannedQuantity <= 0) {
    return err(new InvalidPlannedQuantityError(id));
  }

  // 4. Validate optional BOM reference scope
  const effectiveBomId =
    selectedBomVersionId !== undefined ? selectedBomVersionId : order.selectedBomVersionId;
  if (effectiveBomId) {
    const bom = await db
      .selectFrom("BillOfMaterial")
      .selectAll()
      .where("id", "=", effectiveBomId)
      .executeTakeFirst();

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

  // 5. Validate optional routing reference scope
  const effectiveRoutingId =
    selectedRoutingRevisionId !== undefined
      ? selectedRoutingRevisionId
      : order.selectedRoutingRevisionId;
  if (effectiveRoutingId) {
    const routing = await db
      .selectFrom("Routing")
      .selectAll()
      .where("id", "=", effectiveRoutingId)
      .executeTakeFirst();

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

  // 6. Build update set
  const updateSet: Updateable<"ProductionOrder"> = {
    ...(customFields as Updateable<"ProductionOrder">),
  };

  if (plannedQuantity !== undefined) updateSet.plannedQuantity = plannedQuantity;
  if (plannedStartDate !== undefined) updateSet.plannedStartDate = plannedStartDate;
  if (plannedEndDate !== undefined) updateSet.plannedEndDate = plannedEndDate;
  if (selectedBomVersionId !== undefined) updateSet.selectedBomVersionId = selectedBomVersionId;
  if (selectedRoutingRevisionId !== undefined)
    updateSet.selectedRoutingRevisionId = selectedRoutingRevisionId;

  if (Object.keys(updateSet).length === 0) {
    return ok({ productionOrder: order });
  }

  // 7. Persist changes
  const updated = await db
    .updateTable("ProductionOrder")
    .set(updateSet)
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ productionOrder: updated });
}
