import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  ProductionOrderNotFoundError,
  ProductionOrderNotReschedulableError,
  ExecutionAlreadyStartedError,
  InvalidDateRangeError,
} from "../lib/errors.generated";

export interface RescheduleProductionOrderInput {
  id: string;
  plannedStartDate: Date;
  plannedEndDate: Date;
}

/**
 * Function: rescheduleProductionOrder
 *
 * Changes the planned execution dates on a released order before execution
 * starts. Preserves the released snapshots and keeps the change auditable.
 */
export async function run(
  db: Transaction,
  input: RescheduleProductionOrderInput,
  _ctx: CommandContext,
) {
  const { id, plannedStartDate, plannedEndDate } = 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. Validate status is reschedulable
  if (order.status !== "RELEASED") {
    return err(new ProductionOrderNotReschedulableError(id));
  }

  // 3. Check no work order has execution evidence
  const workOrders = await db
    .selectFrom("WorkOrder")
    .selectAll()
    .where("productionOrderId", "=", id)
    .execute();

  const hasExecution = workOrders.some(
    (wo) =>
      wo.status === "IN_PROGRESS" ||
      wo.status === "PAUSED" ||
      wo.status === "COMPLETE" ||
      wo.completedQuantity > 0 ||
      wo.actualStartDate != null,
  );

  if (hasExecution) {
    return err(new ExecutionAlreadyStartedError(id));
  }

  // 4. Validate revised dates
  if (!plannedStartDate || !plannedEndDate) {
    return err(new InvalidDateRangeError(id));
  }

  const start = new Date(plannedStartDate);
  const end = new Date(plannedEndDate);

  if (isNaN(start.getTime()) || isNaN(end.getTime()) || start >= end) {
    return err(new InvalidDateRangeError(id));
  }

  // 5. Persist scheduling update
  const rescheduled = await db
    .updateTable("ProductionOrder")
    .set({
      plannedStartDate,
      plannedEndDate,
    })
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  return ok({ productionOrder: rescheduled });
}
