import dayjs from "dayjs";
import { ItemLotTraceMaterialInput } from "@/app/api/itemTracing";
import {
  TraceGraphDetailField,
  TraceGraphDocLinkKind,
  TraceGraphNode,
} from "./buildTraceGraphNodes";
import { formatQty } from "./traceQtyUtils";
import { pickStatusValueColor } from "./traceLabelUtils";
import { normalizeTargetDateForLink } from "./traceDocLinkUtils";

export type MaterialPickLabels = {
  nodeMaterialPick: string;
  categoryPick: string;
  pickOrder: string;
  jobOrder: string;
  detailMaterial: string;
  detailLot: string;
  detailQty: string;
  detailType: string;
  detailAssignedStep: string;
  detailPickTargetDate: string;
  detailTime: string;
  processingStatus: string;
  matchStatus: string;
  tr: {
    processingStatus: (code: string | null | undefined) => string;
    matchStatus: (code: string | null | undefined) => string;
  };
};

export const pickStatusDetailFields = (
  labels: Pick<MaterialPickLabels, "processingStatus" | "matchStatus" | "tr">,
  processingStatus?: string | null,
  matchStatus?: string | null,
  opts?: { always?: boolean },
): TraceGraphDetailField[] => {
  const always = opts?.always === true;
  const rows: TraceGraphDetailField[] = [];
  if (always || processingStatus?.trim()) {
    rows.push(
      field(labels.processingStatus, labels.tr.processingStatus(processingStatus), {
        valueColor: pickStatusValueColor(processingStatus),
      }),
    );
  }
  if (always || matchStatus?.trim()) {
    rows.push(
      field(labels.matchStatus, labels.tr.matchStatus(matchStatus), {
        valueColor: pickStatusValueColor(matchStatus),
      }),
    );
  }
  return rows;
};

export const pickStatusNodeLabels = (
  labels: Pick<MaterialPickLabels, "tr">,
  processingStatus?: string | null,
  matchStatus?: string | null,
  opts?: { always?: boolean },
): Pick<
  TraceGraphNode,
  "processingStatusLabel" | "matchStatusLabel" | "processingStatus" | "matchStatus"
> => {
  const always = opts?.always === true;
  const showProcessing = always || Boolean(processingStatus?.trim());
  const showMatch = always || Boolean(matchStatus?.trim());
  return {
    processingStatusLabel: showProcessing
      ? labels.tr.processingStatus(processingStatus)
      : undefined,
    matchStatusLabel: showMatch ? labels.tr.matchStatus(matchStatus) : undefined,
    processingStatus: showProcessing ? processingStatus?.trim() || undefined : undefined,
    matchStatus: showMatch ? matchStatus?.trim() || undefined : undefined,
  };
};

export const fmt = (v: string | null | undefined) => (v?.trim() ? v : "—");

export const field = (
  label: string,
  value: string | null | undefined,
  extra?: Partial<TraceGraphDetailField>,
): TraceGraphDetailField => ({
  label,
  value: fmt(value),
  ...extra,
});

/** Omit detail row when value is blank (used for optional remarks, etc.). */
export const fieldIf = (
  label: string,
  value: string | number | null | undefined,
  extra?: Partial<TraceGraphDetailField>,
): TraceGraphDetailField | null => {
  const raw = value == null ? "" : String(value).trim();
  if (!raw) return null;
  return field(label, raw, extra);
};

export const detailsOf = (
  ...rows: Array<TraceGraphDetailField | null | undefined | false>
): TraceGraphDetailField[] => rows.filter((row): row is TraceGraphDetailField => Boolean(row));

export const parseSortKey = (ts: string | null | undefined, seq: number): number => {
  if (!ts?.trim()) return seq;
  const d = dayjs(ts);
  return d.isValid() ? d.valueOf() : seq;
};

/** Expiry is date-only in master data; sort at end of that calendar day (23:59:59). */
export const parseExpirySortKey = (expiry: string | null | undefined, seq: number): number => {
  if (!expiry?.trim()) return seq;
  const d = dayjs(expiry.trim().slice(0, 10));
  return d.isValid() ? d.endOf("day").valueOf() : seq;
};

export const docLinkFromOriginType = (type: string): TraceGraphDocLinkKind | undefined => {
  const t = type.toUpperCase();
  if (t === "PO") return "po";
  if (t === "JO") return "jo";
  return undefined;
};

export const docLinkFromRefType = (refType: string): TraceGraphDocLinkKind | undefined => {
  const t = refType.toUpperCase();
  if (t === "PO") return "po";
  if (t === "JO") return "jo";
  if (t === "PICK" || t === "DO") return "pick";
  if (t === "JO_PICK") return "jodetail";
  return undefined;
};

/** Semi-finished / nested JO output used as a material for another JO. */
export const isJoProducedMaterial = (m: ItemLotTraceMaterialInput): boolean =>
  (m.productionSteps?.length ?? 0) > 0 ||
  Boolean(m.nestedJoPrelude) ||
  m.stockInOrigin?.type?.trim().toUpperCase() === "JO";

export type MaterialPickNodeContext = {
  nextSeq: () => number;
  pickOrderTargetDate?: (pickOrderCode: string) => string | undefined;
};

export const createMaterialPickNode = (
  m: ItemLotTraceMaterialInput,
  index: number,
  labels: MaterialPickLabels,
  ctx: MaterialPickNodeContext,
  feedsProductionScopeLotNo?: string,
): TraceGraphNode => {
  const matUom = m.materialUom?.trim() || "";
  const pickTitle = m.pickOrderCode || labels.nodeMaterialPick;
  const stepLabel = m.assignedStepName?.trim()
    ? m.bomProcessSeqNo != null
      ? `${m.bomProcessSeqNo}. ${m.assignedStepName}`
      : m.assignedStepName
    : "";
  const itemLabel = [m.materialItemCode, m.materialItemName].filter(Boolean).join(" · ");
  const pickTargetDate = normalizeTargetDateForLink(
    ctx.pickOrderTargetDate?.(m.pickOrderCode ?? ""),
  );
  const pickedDate = normalizeTargetDateForLink(m.pickedAt);
  const linkTargetDate = pickTargetDate || pickedDate;
  return {
    id: `mat-pick-${index}-${m.pickOrderId}-${m.materialLotNo}-${m.pickedAt}`,
    kind: "MATERIAL_PICK",
    timestamp: m.pickedAt,
    sortKey: parseSortKey(m.pickedAt, ctx.nextSeq()),
    title: pickTitle,
    subtitle: [m.materialLotNo, stepLabel, m.consoCode].filter(Boolean).join(" · "),
    qty: m.materialQty,
    uom: matUom,
    meta: itemLabel || undefined,
    refCode: m.pickOrderCode,
    refId: m.pickOrderId,
    consoCode: m.consoCode,
    jobOrderCode: m.jobOrderCode?.trim() || undefined,
    docLinkKind: m.pickOrderCode ? "jodetail" : undefined,
    docLinkTargetDate: linkTargetDate,
    traceLotNo: m.materialLotNo,
    traceItemCode: m.materialItemCode,
    bomProcessId: m.bomProcessId,
    bomProcessSeqNo: m.bomProcessSeqNo,
    assignedStepName: m.assignedStepName,
    feedsProductionScopeLotNo,
    categoryLabel: labels.categoryPick,
    details: [
      field(labels.pickOrder, m.pickOrderCode, {
        linkKind: "jodetail",
        linkCode: m.pickOrderCode,
        linkId: m.pickOrderId,
        consoCode: m.consoCode,
        linkTargetDate,
      }),
      field(labels.detailPickTargetDate, pickTargetDate || pickedDate),
      field(labels.detailMaterial, `${m.materialItemCode} · ${m.materialItemName}`),
      field(labels.detailLot, m.materialLotNo),
      field(labels.detailQty, formatQty(m.materialQty, matUom)),
      field(labels.detailType, labels.nodeMaterialPick),
      field(labels.detailAssignedStep, stepLabel || "—"),
      field(labels.jobOrder, m.jobOrderCode, {
        linkKind: "jo",
        linkCode: m.jobOrderCode,
        linkId: m.jobOrderId,
      }),
      ...pickStatusDetailFields(labels, m.processingStatus, m.matchStatus),
      field(labels.detailTime, m.pickedAt),
    ],
    ...pickStatusNodeLabels(labels, m.processingStatus, m.matchStatus),
  };
};
