import { ItemLotTraceResponse } from "@/app/api/itemTracing";
import dayjs from "dayjs";
import {
  TraceGraphDetailLabels,
  TraceGraphNode,
  TraceGraphBuildScope,
  scopePrefix,
  withNodeScope,
} from "./buildTraceGraphNodes";
import { formatQty } from "./traceQtyUtils";
import {
  formatDoOutboundKindLabel,
  resolveDoOutboundChipFlags,
} from "./traceLabelUtils";
import { resolveDoOutboundDocLink } from "./traceDocLinkUtils";
import { field, fieldIf, detailsOf, parseSortKey } from "./traceNodeFactory";

export interface ExtendedTraceGraphLabels extends TraceGraphDetailLabels {
  nodeOpen: string;
  nodeFail: string;
  nodeDoOut: string;
  nodeReplenishmentCreated: string;
  nodeReturn: string;
  nodeRepack: string;
  traceRepackLot: string;
  doOutboundExtra: string;
  doOutboundReplenish: string;
  detailDoOutboundKind: string;
}

const shopDisplayLabel = (shopCode?: string, shopName?: string): string => {
  const code = shopCode?.trim();
  const name = shopName?.trim();
  if (code && name) return `${code} · ${name}`;
  return name || code || "";
};

export const buildExtendedTraceGraphNodes = (
  data: ItemLotTraceResponse,
  labels: ExtendedTraceGraphLabels,
  scope?: TraceGraphBuildScope,
): TraceGraphNode[] => {
  const nodes: TraceGraphNode[] = [];
  let seq = 0;
  const stockUom = data.lot.uom;
  const idPfx = scopePrefix(scope);
  const lb = scope?.locationBlockKeys ?? false;

  (data.openMovements ?? []).forEach((o, i) => {
    nodes.push({
      id: lb ? `${idPfx}open-${i}-${o.stockInLineId}` : `${idPfx}open-${o.stockInLineId}`,
      kind: "OPEN",
      timestamp: o.timestamp,
      sortKey: parseSortKey(o.timestamp, seq++),
      title: labels.nodeOpen,
      subtitle: [o.refCode, o.warehouseCode].filter(Boolean).join(" · ") || "—",
      qty: o.qty,
      uom: stockUom,
      refCode: o.refCode,
      categoryLabel: labels.categoryOpen,
      details: detailsOf(
        field(labels.detailType, labels.nodeOpen),
        field(labels.detailInboundSiNo, o.refCode),
        field(labels.detailWarehouse, o.warehouseCode),
        field(labels.detailQty, formatQty(o.qty, stockUom)),
        field(labels.detailHandler, o.handledBy),
        field(labels.detailTime, o.timestamp),
        fieldIf(labels.detailRemarks, o.remarks),
      ),
    });
  });

  (data.failEvents ?? []).forEach((f, i) => {
    nodes.push({
      id: lb ? `${idPfx}fail-${i}-${f.failId}` : `${idPfx}fail-${f.failId}`,
      kind: "FAIL",
      timestamp: f.recordDate,
      sortKey: parseSortKey(f.recordDate, seq++),
      title: labels.nodeFail,
      subtitle: [labels.tr.failType(f.failType), f.category].filter(Boolean).join(" · ") || "—",
      qty: f.qty,
      uom: stockUom,
      refType: "JO_PICK",
      refCode: f.pickOrderCode,
      refId: f.pickOrderId,
      docLinkKind: f.pickOrderCode ? "jodetail" : undefined,
      consoCode: f.pickOrderCode,
      docLinkTargetDate: f.recordDate?.trim()
        ? dayjs(f.recordDate).isValid()
          ? dayjs(f.recordDate).format("YYYY-MM-DD")
          : f.recordDate.trim().slice(0, 10)
        : undefined,
      categoryLabel: labels.categoryQc,
      details: detailsOf(
        field(labels.detailType, labels.nodeFail),
        field(labels.detailStatus, labels.tr.failType(f.failType)),
        field(labels.pickOrder, f.pickOrderCode, {
          linkKind: f.pickOrderCode ? "jodetail" : undefined,
          linkCode: f.pickOrderCode,
          linkId: f.pickOrderId,
          consoCode: f.pickOrderCode,
          linkTargetDate: f.recordDate?.trim()
            ? dayjs(f.recordDate).isValid()
              ? dayjs(f.recordDate).format("YYYY-MM-DD")
              : f.recordDate.trim().slice(0, 10)
            : undefined,
        }),
        field(labels.detailQty, formatQty(f.qty, stockUom)),
        field(labels.detailHandler, f.handlerName),
        field(labels.detailTime, f.recordDate),
        fieldIf(labels.detailFailCategory, f.category),
      ),
    });
  });

  (data.replenishmentEvents ?? []).forEach((r) => {
    const supplyTo = shopDisplayLabel(r.shopCode, r.shopName);
    nodes.push({
      id: `${idPfx}replenish-${r.replenishmentId}`,
      kind: "REPLENISHMENT_CREATED",
      timestamp: r.timestamp,
      sortKey: parseSortKey(r.timestamp, seq++),
      title: labels.nodeReplenishmentCreated,
      subtitle: r.sourceDoCode || r.replenishmentCode || "—",
      qty: r.replenishQty,
      uom: stockUom,
      refType: "DO",
      refCode: r.sourceDoCode,
      refId: r.sourceDoId,
      replenishmentStockOutLineId: r.stockOutLineId ?? undefined,
      categoryLabel: labels.categoryOutbound,
      details: detailsOf(
        field(labels.detailType, labels.nodeReplenishmentCreated),
        field(labels.deliveryOrder, r.sourceDoCode),
        field(labels.detailSupplyTo, supplyTo || "—"),
        field(labels.detailItemCode, r.itemNo),
        field(labels.detailItemName, r.itemName),
        field(labels.detailReplenishmentCode, r.replenishmentCode),
        field(labels.detailQty, formatQty(r.replenishQty, stockUom)),
        field(labels.detailReason, r.reason),
        field(labels.detailHandler, r.handler),
        field(labels.detailTime, r.timestamp),
      ),
    });
  });

  (data.doDeliveries ?? []).forEach((d, i) => {
    const supplyTo = shopDisplayLabel(d.shopCode, d.shopName);
    const kindLabels = {
      extra: labels.doOutboundExtra,
      replenish: labels.doOutboundReplenish,
    };
    const chipFlags = resolveDoOutboundChipFlags({
      isExtra: d.isExtra,
      isReplenish: d.isReplenish,
      ticketNo: d.ticketNo,
      consoCode: d.consoCode,
      releaseType: d.releaseType,
      deliveryOrderPickOrderId: d.deliveryOrderPickOrderId,
      relationshipId: d.relationshipId,
    });
    const outboundKindLabel = formatDoOutboundKindLabel(
      chipFlags.isExtra,
      chipFlags.isReplenish,
      kindLabels,
    );
    const doCode = d.deliveryOrderCode || "—";
    const docLink = resolveDoOutboundDocLink({
      pickOrderCode: d.pickOrderCode,
      pickOrderId: d.pickOrderId,
      deliveryOrderCode: d.deliveryOrderCode,
      ticketNo: d.ticketNo,
      consoCode: d.consoCode,
      timestamp: d.timestamp,
      deliveryOrderPickOrderId: d.deliveryOrderPickOrderId,
    });
    nodes.push({
      id: lb ? `${idPfx}dodel-${i}-${d.stockOutLineId}` : `${idPfx}do-${d.stockOutLineId}`,
      kind: "DO_OUT",
      timestamp: d.timestamp,
      sortKey: parseSortKey(d.timestamp, seq++),
      title: `${labels.nodeDoOut} · ${doCode}`,
      subtitle: "",
      qty: d.qty,
      uom: stockUom,
      refType: "DO",
      refCode: docLink.displayCode,
      refId: d.deliveryOrderId ?? d.pickOrderId,
      docLinkKind: docLink.kind,
      docLinkTicketNo: docLink.ticketNo,
      docLinkTargetDate: docLink.targetDate,
      consoCode: docLink.consoCode || d.consoCode || d.pickOrderCode,
      categoryLabel: labels.categoryOutbound,
      doOutboundIsExtra: chipFlags.isExtra,
      doOutboundIsReplenish: chipFlags.isReplenish,
      doOutboundQtyChanged: d.qtyChanged === true,
      outboundKindLabel,
      warehouseCode: scope?.defaultWarehouseCode,
      details: [
        field(labels.detailType, labels.nodeDoOut),
        ...(outboundKindLabel
          ? [field(labels.detailDoOutboundKind, outboundKindLabel)]
          : []),
        field(labels.deliveryOrder, d.deliveryOrderCode),
        ...(d.deliveryNoteCode?.trim()
          ? [field(labels.deliveryNoteCode, d.deliveryNoteCode.trim())]
          : []),
        ...(d.ticketNo?.trim()
          ? [field(labels.ticketNo, d.ticketNo.trim())]
          : []),
        field(labels.detailSupplyTo, supplyTo || "—"),
        field(labels.pickOrder, d.pickOrderCode, {
          linkKind: docLink.kind,
          linkCode: d.pickOrderCode,
          linkId: d.pickOrderId,
          consoCode: docLink.consoCode || d.consoCode || d.pickOrderCode,
          linkTicketNo: docLink.ticketNo,
          linkTargetDate: docLink.targetDate,
        }),
        field(labels.detailQty, formatQty(d.qty, stockUom)),
        field(labels.detailHandler, d.handler),
        field(labels.detailTime, d.timestamp),
      ],
    });
  });

  (data.returnEvents ?? []).forEach((r, i) => {
    nodes.push({
      id: lb
        ? `${idPfx}return-${i}-${r.stockOutLineId}`
        : `${idPfx}return-${r.stockOutLineId}-${r.timestamp}`,
      kind: "RETURN",
      timestamp: r.timestamp,
      sortKey: parseSortKey(r.timestamp, seq++),
      title: labels.nodeReturn,
      subtitle: [labels.tr.movementType(r.movementType), r.refCode].filter(Boolean).join(" · ") || "—",
      qty: r.qty,
      uom: stockUom,
      refCode: r.refCode,
      categoryLabel: labels.categoryOutbound,
      details: detailsOf(
        field(labels.detailType, labels.tr.movementType(r.movementType)),
        field(labels.detailReturnRef, r.refCode),
        field(labels.detailWarehouse, r.warehouseCode),
        field(labels.detailQty, formatQty(r.qty, stockUom)),
        field(labels.detailHandler, r.handler),
        field(labels.detailTime, r.timestamp),
        fieldIf(labels.detailRemarks, r.remarks),
      ),
    });
  });

  (data.lotRelations ?? []).forEach((rel, i) => {
    nodes.push({
      id: `repack-${rel.inventoryLotId ?? i}-${rel.lotNo}`,
      kind: "REPACK",
      timestamp: rel.timestamp,
      sortKey: parseSortKey(rel.timestamp, seq++),
      title: `${rel.itemCode} · ${labels.nodeRepack}`,
      subtitle: [rel.lotNo, rel.productLotNo].filter(Boolean).join(" · ") || rel.itemName,
      qty: rel.qty,
      uom: stockUom,
      traceLotNo: rel.lotNo,
      traceItemCode: rel.itemCode,
      categoryLabel: labels.categoryTransfer,
      details: [
        field(labels.detailType, labels.nodeRepack),
        field(labels.detailMaterial, `${rel.itemCode} · ${rel.itemName}`),
        field(labels.detailLot, rel.lotNo),
        field(labels.detailQty, formatQty(rel.qty, stockUom)),
        field(labels.detailProductLotNo, rel.productLotNo),
        field(labels.detailTime, rel.timestamp),
      ],
    });
  });

  return nodes.map((n) => withNodeScope(n, scope));
};
