import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/core";
import type { Transaction } from "../generated/kysely-tailordb";
import {
  WorkOrderNotFoundError,
  WorkOrderNotReportableError,
  InvalidReportedQuantityError,
  EmptyProgressTransactionError,
  ScrapHandoffRequiredError,
} from "../lib/errors.generated";

export interface ReportWorkOrderProgressInput {
  id: string;
  completedQuantity?: number;
  scrapQuantity?: number;
  actualSetupTime?: number;
  actualRunTime?: number;
  notes?: string | null;
  scrapHandoffData?: Record<string, unknown> | null;
}

/**
 * Function: reportWorkOrderProgress
 *
 * Records partial execution evidence such as completed quantity, scrap quantity,
 * actual time, and exception notes. Emits ManufacturingScrapHandoff when the
 * report contains scrapped quantity.
 */
export async function run(
  db: Transaction,
  input: ReportWorkOrderProgressInput,
  _ctx: CommandContext,
) {
  const {
    id,
    completedQuantity = 0,
    scrapQuantity = 0,
    actualSetupTime = 0,
    actualRunTime = 0,
    notes = null,
    scrapHandoffData = null,
  } = input;

  // 1. Validate quantities are non-negative
  if (completedQuantity < 0 || scrapQuantity < 0) {
    return err(new InvalidReportedQuantityError(id));
  }

  // 2. Validate at least one positive value was reported
  if (completedQuantity <= 0 && scrapQuantity <= 0 && actualSetupTime <= 0 && actualRunTime <= 0) {
    return err(new EmptyProgressTransactionError(id));
  }

  // 3. Fetch work order with lock
  const workOrder = await db
    .selectFrom("WorkOrder")
    .selectAll()
    .where("id", "=", id)
    .forUpdate()
    .executeTakeFirst();

  if (!workOrder) {
    return err(new WorkOrderNotFoundError(id));
  }

  // 4. Validate status is IN_PROGRESS
  if (workOrder.status !== "IN_PROGRESS") {
    return err(new WorkOrderNotReportableError(id));
  }

  // 5. Validate scrap handoff when positive scrap is reported
  if (scrapQuantity > 0 && !scrapHandoffData) {
    return err(new ScrapHandoffRequiredError(id));
  }

  // 6. Accumulate quantities and time
  const now = new Date();

  const updatedWorkOrder = await db
    .updateTable("WorkOrder")
    .set({
      completedQuantity: workOrder.completedQuantity + completedQuantity,
      scrapQuantity: workOrder.scrapQuantity + scrapQuantity,
      actualSetupTime: workOrder.actualSetupTime + actualSetupTime,
      actualRunTime: workOrder.actualRunTime + actualRunTime,
      executionNotes: notes ?? workOrder.executionNotes,
    })
    .where("id", "=", id)
    .returningAll()
    .executeTakeFirstOrThrow();

  // 7. Create PROGRESS_REPORTED execution event
  await db
    .insertInto("WorkOrderExecutionEvent")
    .values({
      workOrderId: id,
      eventType: "PROGRESS_REPORTED",
      timestamp: now,
      quantity: completedQuantity,
      timeValue: actualSetupTime + actualRunTime,
      scrapValue: scrapQuantity > 0 ? scrapQuantity : null,
      notes,
    })
    .execute();

  return ok({ workOrder: updatedWorkOrder });
}
