import type { Transaction } from "../generated/kysely-tailordb";
import {
  RoutingItemNotFoundError,
  OperationRequiredError,
  DuplicateOperationSequenceError,
  InvalidStandardTimeError,
  WorkCenterNotFoundError,
  CrossCompanyWorkCenterError,
} from "../lib/errors.generated";
import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";

export interface CreateRoutingOperationInput {
  sequenceNumber: number;
  operationDescription?: string | null;
  workCenterId: string;
  standardSetupTime: number;
  standardRunTime: number;
  operatorInstructions?: string | null;
}

export interface CreateRoutingInput {
  parentItemId: string;
  companyId: string;
  siteId?: string | null;
  revisionNumber?: string | null;
  operations: CreateRoutingOperationInput[];
}

/**
 * Function: createRouting
 *
 * Creates a new routing in DRAFT status with ordered operations.
 * Validates parent item existence, operation sequence uniqueness,
 * standard times, and work center references.
 */
export async function run<CF extends Record<string, unknown>>(
  db: Transaction,
  input: CreateRoutingInput & CF,
  _ctx: CommandContext,
) {
  const { parentItemId, companyId, siteId, revisionNumber, operations, ...customFields } = input;

  // 1. Validate parent item exists
  const parentItem = await db
    .selectFrom("Item")
    .selectAll()
    .where("id", "=", parentItemId)
    .executeTakeFirst();

  if (!parentItem) {
    return err(new RoutingItemNotFoundError(parentItemId));
  }

  // 2. Validate at least one operation
  if (!operations || operations.length === 0) {
    return err(new OperationRequiredError(parentItemId));
  }

  // 3. Validate unique sequence numbers
  const seqNumbers = operations.map((op) => op.sequenceNumber);
  const uniqueSeqs = new Set(seqNumbers);
  if (uniqueSeqs.size !== seqNumbers.length) {
    return err(new DuplicateOperationSequenceError(parentItemId));
  }

  // 4. Validate standard times >= 0
  for (const op of operations) {
    if (op.standardSetupTime < 0 || op.standardRunTime < 0) {
      return err(new InvalidStandardTimeError(parentItemId));
    }
  }

  // 5. Validate work centers exist and belong to same company
  for (const op of operations) {
    const workCenter = await db
      .selectFrom("WorkCenter")
      .selectAll()
      .where("id", "=", op.workCenterId)
      .executeTakeFirst();

    if (!workCenter) {
      return err(new WorkCenterNotFoundError(op.workCenterId));
    }

    if (workCenter.companyId !== companyId) {
      return err(new CrossCompanyWorkCenterError(op.workCenterId));
    }
  }

  // 6. Create routing in DRAFT status
  const routing = await db
    .insertInto("Routing")
    .values({
      ...(customFields as Record<string, unknown>),
      parentItemId,
      companyId,
      siteId: siteId ?? null,
      revisionNumber: revisionNumber ?? null,
      status: "DRAFT",
      createdAt: new Date(),
      updatedAt: null,
    })
    .returningAll()
    .executeTakeFirst();

  // 7. Create routing operations
  const createdOperations = [];
  for (const op of operations) {
    const operation = await db
      .insertInto("RoutingOperation")
      .values({
        routingId: routing!.id,
        sequenceNumber: op.sequenceNumber,
        operationDescription: op.operationDescription ?? null,
        workCenterId: op.workCenterId,
        standardSetupTime: op.standardSetupTime,
        standardRunTime: op.standardRunTime,
        operatorInstructions: op.operatorInstructions ?? null,
        createdAt: new Date(),
        updatedAt: null,
      })
      .returningAll()
      .executeTakeFirst();
    createdOperations.push(operation!);
  }

  return ok({ routing: routing!, routingOperations: createdOperations });
}
