import type { ItemLotTraceResponse } from "@/app/api/itemTracing";
import type { TraceGraphLayoutNode } from "./traceGraphLayout";
import type { TraceGraphNodeKind } from "./buildTraceGraphNodes";
import { mergeScopedMovements } from "./mergeLocationScopedData";

export type TraceMovementRow = {
  id: string;
  direction: string;
  qty: number;
  movementType: string;
  refType: string;
  refCode: string;
  refId: number | null;
  warehouseCode: string;
  handledBy: string;
  timestamp: string | null;
  remarks: string;
};

export type TraceJoPickRow = {
  id: string;
  pickOrderCode: string;
  pickOrderId: number | null;
  consoCode: string;
  materialItemCode: string;
  materialLotNo: string;
  qty: number;
  uom: string;
  pickedAt: string | null;
  bomProcessId: number | null;
  assignedStepName: string;
  /** YYYY-MM-DD for jodetail deep links. */
  targetDate?: string;
};

export type TraceProductionRow = {
  id: string;
  stepName: string;
  description: string;
  equipmentCode: string;
  equipmentName: string;
  operatorName: string;
  startTime: string | null;
  endTime: string | null;
  outputQty: number;
  scrapQty: number;
  defectQty: number;
  status: string;
  seqNo: number | null;
  bomProcessId: number | null;
  traceLotNo: string | null;
};

const detailValue = (node: TraceGraphLayoutNode, labelIncludes: string): string => {
  const row = node.details.find((d) => d.label.includes(labelIncludes));
  return row?.value?.trim() || "—";
};

const movementKinds: TraceGraphNodeKind[] = [
  "IN",
  "OUT",
  "RECEIPT",
  "OPEN",
  "RETURN",
  "REPACK",
];

export const deriveMovementRowsFromGraph = (
  nodes: TraceGraphLayoutNode[],
  data: ItemLotTraceResponse,
): TraceMovementRow[] => {
  const fromGraph = nodes
    .filter((n) => movementKinds.includes(n.kind))
    .map((n) => ({
      id: n.id,
      direction: n.kind === "OUT" || n.kind === "RETURN" ? "OUT" : "IN",
      qty: n.qty ?? 0,
      movementType: n.subtitle?.trim() || n.kind,
      refType: n.refType?.trim() || "",
      refCode: n.refCode?.trim() || "",
      refId: n.refId ?? null,
      warehouseCode: n.warehouseCode?.trim() || (detailValue(n, "Warehouse") !== "—" ? detailValue(n, "Warehouse") : n.meta?.split(" · ")[1] ?? ""),
      handledBy: detailValue(n, "Handler") !== "—" ? detailValue(n, "Handler") : n.meta?.split(" · ")[0] ?? "",
      timestamp: n.timestamp,
      remarks: detailValue(n, "Remarks"),
    }));

  if (fromGraph.length > 0) return fromGraph;

  return mergeScopedMovements(data).map((m) => ({
    id: m.rowKey,
    direction: m.direction,
    qty: m.qty,
    movementType: m.movementType,
    refType: m.refType,
    refCode: m.refCode,
    refId: m.refId,
    warehouseCode: m.scopeWarehouseCode,
    handledBy: m.handledBy,
    timestamp: m.timestamp,
    remarks: m.remarks,
  }));
};

export const deriveJoPickRowsFromGraph = (
  nodes: TraceGraphLayoutNode[],
  fallbackInputs: NonNullable<ItemLotTraceResponse["joPrelude"]>["materialInputs"] | undefined,
): TraceJoPickRow[] => {
  const picks = nodes.filter((n) => n.kind === "MATERIAL_PICK");
  if (picks.length > 0) {
    return picks.map((n) => ({
      id: n.id,
      pickOrderCode: n.refCode?.trim() || n.title?.trim() || "",
      pickOrderId: n.refId ?? null,
      consoCode: n.consoCode?.trim() || "",
      materialItemCode: n.traceItemCode?.trim() || "",
      materialLotNo: n.traceLotNo?.trim() || "",
      qty: n.qty ?? 0,
      uom: n.uom?.trim() || "",
      pickedAt: n.timestamp,
      bomProcessId: n.bomProcessId ?? null,
      assignedStepName: n.assignedStepName?.trim() || "",
      targetDate: n.docLinkTargetDate?.trim() || undefined,
    }));
  }

  return (fallbackInputs ?? []).map((m, i) => ({
    id: `pick-api-${i}-${m.pickOrderCode}-${m.materialLotNo}`,
    pickOrderCode: m.pickOrderCode,
    pickOrderId: m.pickOrderId,
    consoCode: m.consoCode,
    materialItemCode: m.materialItemCode,
    materialLotNo: m.materialLotNo,
    qty: m.materialQty,
    uom: m.materialUom ?? "",
    pickedAt: m.pickedAt,
    bomProcessId: m.bomProcessId ?? null,
    assignedStepName: m.assignedStepName ?? "",
    targetDate: undefined,
  }));
};

export const deriveProductionRowsFromGraph = (
  nodes: TraceGraphLayoutNode[],
  fallback: ItemLotTraceResponse["productionSteps"],
): TraceProductionRow[] => {
  const steps = nodes.filter((n) => n.kind === "PRODUCTION_STEP");
  if (steps.length > 0) {
    return steps.map((n) => ({
      id: n.id,
      stepName: n.title?.trim() || detailValue(n, "Step"),
      description: detailValue(n, "Remarks"),
      equipmentCode: "",
      equipmentName: detailValue(n, "Equipment"),
      operatorName: detailValue(n, "Handler"),
      startTime: null,
      endTime: n.timestamp,
      outputQty: n.qty ?? 0,
      scrapQty: 0,
      defectQty: 0,
      status: detailValue(n, "Status"),
      seqNo: n.bomProcessSeqNo ?? null,
      bomProcessId: n.bomProcessId ?? null,
      traceLotNo: n.traceLotNo ?? null,
    }));
  }

  return fallback.map((s) => ({
    id: `prod-api-${s.processLineId}`,
    stepName: s.stepName,
    description: s.description,
    equipmentCode: s.equipmentCode,
    equipmentName: s.equipmentName,
    operatorName: s.operatorName,
    startTime: s.startTime,
    endTime: s.endTime,
    outputQty: s.outputQty,
    scrapQty: s.scrapQty,
    defectQty: s.defectQty,
    status: s.status,
    seqNo: s.seqNo,
    bomProcessId: s.bomProcessId ?? null,
    traceLotNo: null,
  }));
};

export type TracePresentationRows = {
  movements: TraceMovementRow[];
  joPicks: TraceJoPickRow[];
  production: TraceProductionRow[];
};

export const buildTracePresentationRows = (
  nodes: TraceGraphLayoutNode[],
  data: ItemLotTraceResponse,
): TracePresentationRows => ({
  movements: deriveMovementRowsFromGraph(nodes, data),
  joPicks: deriveJoPickRowsFromGraph(nodes, data.joPrelude?.materialInputs),
  production: deriveProductionRowsFromGraph(nodes, data.productionSteps),
});
