import {
  ItemLotTraceByproductLot,
  ItemLotTraceProductionStep,
} from "@/app/api/itemTracing";
import {
  TraceGraphDetailLabels,
  TraceGraphNode,
} from "./buildTraceGraphNodes";
import { formatQty } from "./traceQtyUtils";
import { field, fieldIf, detailsOf, parseSortKey } from "./traceNodeFactory";

export interface ProductionGraphLabels extends TraceGraphDetailLabels {
  nodeProductionStep: string;
  nodeByproduct: string;
  nodeScrap: string;
  nodeDefect: string;
  detailProcessOutputQty: string;
  detailProcessScrapQty: string;
  detailProcessDefectQty: string;
}

const formatStepMaterials = (
  materials: ItemLotTraceProductionStep["stepMaterials"],
): string => {
  if (!materials?.length) return "—";
  return materials
    .map((mat, i) => {
      const label = [mat.materialItemCode, mat.materialItemName].filter(Boolean).join(" · ");
      const qty = formatQty(mat.qtyPerUnit, mat.uom);
      return `${i + 1}. ${label} (${qty})`;
    })
    .join("\n");
};

export const buildProductionGraphNodes = (
  productionSteps: ItemLotTraceProductionStep[],
  byproductLots: ItemLotTraceByproductLot[],
  labels: ProductionGraphLabels,
  lotUom: string,
): TraceGraphNode[] => {
  const nodes: TraceGraphNode[] = [];
  let seq = 0;

  productionSteps.forEach((step) => {
    const ts = step.endTime ?? step.startTime;
    const equip = [step.equipmentCode, step.equipmentName].filter(Boolean).join(" ");
    const stepMaterialsLabel = formatStepMaterials(step.stepMaterials);
    const materialCodesList = (step.stepMaterials ?? [])
      .map((mat) => mat.materialItemCode?.trim())
      .filter((code): code is string => Boolean(code));
    const materialCodes = materialCodesList.join(" · ");
    nodes.push({
      id: `prod-step-${step.processLineId}`,
      kind: "PRODUCTION_STEP",
      timestamp: ts,
      sortKey: parseSortKey(ts, seq++),
      title: step.stepName || labels.nodeProductionStep,
      subtitle: [materialCodes, equip, labels.tr.productionStatus(step.status)]
        .filter(Boolean)
        .join(" · ") || "—",
      qty: step.outputQty,
      uom: lotUom,
      meta: step.operatorName ? `${labels.detailHandler}: ${step.operatorName}` : undefined,
      refType: "JO",
      bomProcessId: step.bomProcessId,
      bomProcessSeqNo: step.seqNo,
      assignedStepName: step.stepName,
      stepMaterialItemCodes: materialCodesList.length ? materialCodesList : undefined,
      categoryLabel: labels.categoryProduction,
      details: detailsOf(
        field(labels.detailType, labels.nodeProductionStep),
        field(labels.detailStatus, labels.tr.productionStatus(step.status)),
        field(labels.detailProcessStep, step.stepName),
        field(labels.detailStepMaterials, stepMaterialsLabel),
        fieldIf(labels.detailProcessDescription, step.description),
        field(labels.detailHandler, step.operatorName),
        field(labels.detailTime, ts),
        field(labels.detailProcessOutputQty, formatQty(step.outputQty, lotUom)),
        field(labels.detailProcessScrapQty, formatQty(step.scrapQty, lotUom)),
        field(labels.detailProcessDefectQty, formatQty(step.defectQty, lotUom)),
        field(labels.detailEquipment, equip || "—"),
      ),
    });

    const lossQty = (step.scrapQty ?? 0) + (step.defectQty ?? 0);
    if (lossQty > 0) {
      if ((step.scrapQty ?? 0) > 0) {
        nodes.push({
          id: `scrap-${step.processLineId}`,
          kind: "SCRAP",
          timestamp: ts,
          sortKey: parseSortKey(ts, seq++) + 1,
          title: labels.nodeScrap,
          subtitle: step.stepName || "—",
          qty: step.scrapQty,
          uom: lotUom,
          bomProcessId: step.bomProcessId,
          bomProcessSeqNo: step.seqNo,
          assignedStepName: step.stepName,
          categoryLabel: labels.categoryProduction,
          details: [
            field(labels.detailType, labels.nodeScrap),
            field(labels.detailProcessStep, step.stepName),
            field(labels.detailProcessScrapQty, formatQty(step.scrapQty, lotUom)),
            field(labels.detailTime, ts),
          ],
        });
      }
      if ((step.defectQty ?? 0) > 0) {
        nodes.push({
          id: `defect-${step.processLineId}`,
          kind: "DEFECT",
          timestamp: ts,
          sortKey: parseSortKey(ts, seq++) + 2,
          title: labels.nodeDefect,
          subtitle: step.stepName || "—",
          qty: step.defectQty,
          uom: lotUom,
          bomProcessId: step.bomProcessId,
          bomProcessSeqNo: step.seqNo,
          assignedStepName: step.stepName,
          categoryLabel: labels.categoryProduction,
          details: [
            field(labels.detailType, labels.nodeDefect),
            field(labels.detailProcessStep, step.stepName),
            field(labels.detailProcessDefectQty, formatQty(step.defectQty, lotUom)),
            field(labels.detailTime, ts),
          ],
        });
      }
    }
  });

  byproductLots.forEach((bp, i) => {
    const subtitle = [bp.lotNo, bp.processStepName].filter(Boolean).join(" · ") || bp.itemName;
    nodes.push({
      id: `byproduct-${bp.inventoryLotId ?? i}-${bp.lotNo}`,
      kind: "BYPRODUCT",
      timestamp: bp.producedAt,
      sortKey: parseSortKey(bp.producedAt, seq++),
      title: `${bp.itemCode} · ${labels.nodeByproduct}`,
      subtitle,
      qty: bp.qty,
      uom: bp.uom,
      refType: "JO",
      refCode: bp.jobOrderCode,
      refId: bp.jobOrderId,
      docLinkKind: "jo",
      traceLotNo: bp.lotNo,
      traceItemCode: bp.itemCode,
      categoryLabel: labels.categoryProduction,
      details: [
        field(labels.detailType, labels.nodeByproduct),
        field(labels.detailMaterial, `${bp.itemCode} · ${bp.itemName}`),
        field(labels.detailLot, bp.lotNo),
        field(labels.detailQty, formatQty(bp.qty, bp.uom)),
        field(labels.detailTime, bp.producedAt),
        field(labels.jobOrder, bp.jobOrderCode, {
          linkKind: "jo",
          linkCode: bp.jobOrderCode,
          linkId: bp.jobOrderId,
        }),
        field(labels.detailProcessStep, bp.processStepName),
      ],
    });
  });

  return nodes;
};
