// Perdura — Concentration & Health panel (Stage 10, Phase 3).
//
// A GENERIC, config-driven renderer. NO industry/customer/archetype branching:
// values come from the evaluator's computed signals (HHI, DPO benchmark) and the
// tenant's subledger analytics + aging. Missing subledgers render honest "Awaiting…"
// states (Phase 4 upgrades these to proper data-unlock callouts).
//
//   <window.ConcentrationHealthPanel data={data} signals={set.signals}
//      setPage={setPage} setDrillKey={setDrillKey} />
//
// LEFT  — customer/revenue: top-5 share, customer HHI + threshold legend, AR aging.
// RIGHT — vendor/AP:        top-5 share, vendor HHI + legend, AP aging, DPO-vs-bench flag.

(function () {
  const R = window.React;
  if (!R) return;

  const fmtUSD = (n) => {
    const F = window.PerduraFmt;
    if (window.fmtUSD) return window.fmtUSD(n, { compact: true });
    if (F && F.fmtUSD) return F.fmtUSD(n, { compact: true });
    return "$" + Math.round(n || 0).toLocaleString();
  };
  const pct1 = (x) => (x * 100).toFixed(1) + "%";

  // Aging bucket palette — green (freshest) → red (oldest).
  const BUCKET_COLORS = ["var(--positive,#047857)", "var(--accent,#2f6fed)", "var(--warning,#b7791f)", "#e67e22", "var(--danger,#c0392b)"];

  // AR aging-viz bucket labels → the AR/AP drill modal's bucket-filter keys (Phase 7).
  // AR maps cleanly; AP aging buckets use different day boundaries (15/30 vs 30/60/90),
  // so AP segments open the AP detail unfiltered rather than mis-filtering.
  const AR_BUCKET_MAP = { "Current": "Current", "1–30": "1-30", "31–60": "31-60", "61–90": "61-90", ">90": "90+" };

  // First matching signal id (handles the customer/revenue HHI fallback).
  function sigBy(signals, ids) {
    if (!Array.isArray(signals)) return null;
    for (let i = 0; i < ids.length; i++) { const s = signals.find((x) => x.id === ids[i]); if (s) return s; }
    return null;
  }

  // Catalog HHI is fractional [0,1]; show it on the conventional 0–10000 scale.
  function hhiBand(hhi10k) {
    if (hhi10k < 1500) return { label: "Healthy", color: "var(--positive,#047857)" };
    if (hhi10k <= 2500) return { label: "Warning", color: "var(--warning,#b7791f)" };
    return { label: "Concentrated risk", color: "var(--danger,#c0392b)" };
  }

  function note(text) {
    return R.createElement("div", { style: { fontSize: 12.5, color: "var(--text-3)", lineHeight: 1.55, padding: "6px 0" } }, text);
  }

  function subhead(t) {
    return R.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.5, color: "var(--text-2)", marginBottom: 6 } }, t);
  }

  function HhiRow({ signal }) {
    if (!signal || signal.value == null) return note("Awaiting subledger data to compute concentration (HHI).");
    const hhi10k = Math.round(signal.value * 10000);
    const band = hhiBand(hhi10k);
    return R.createElement("div", null,
      R.createElement("div", { style: { display: "flex", alignItems: "baseline", gap: 10, flexWrap: "wrap" } },
        R.createElement("span", { style: { fontSize: 22, fontWeight: 800, color: "var(--text-1)" } }, String(hhi10k)),
        R.createElement("span", { style: { fontSize: 11.5, fontWeight: 700, color: band.color, textTransform: "uppercase", letterSpacing: 0.4 } }, band.label)),
      R.createElement("div", { style: { fontSize: 10.5, color: "var(--text-3)", marginTop: 3 } }, "HHI · <1500 healthy · 1500–2500 warning · >2500 concentrated"));
  }

  // Inline stacked aging bar (self-contained — no external chart dependency).
  function AgingBar({ side, label, onSeg }) {
    if (!side || side.hasSubledger === false || !Array.isArray(side.buckets) || !side.buckets.length) {
      return note("Awaiting " + label + " subledger — aging populates once it's ingested.");
    }
    const total = side.outstanding || side.buckets.reduce((s, b) => s + (b.value || 0), 0);
    if (!total) return note("No open " + label + " balance in the current window.");
    const segs = side.buckets.map((b, i) => ({
      label: b.label, value: b.value || 0,
      color: BUCKET_COLORS[Math.min(i, BUCKET_COLORS.length - 1)],
      share: (b.value || 0) / total,
    }));
    const oldest = segs[segs.length - 1];
    const stressed = oldest && oldest.share > 0.05;
    return R.createElement("div", null,
      R.createElement("div", { style: { display: "flex", height: 14, borderRadius: 6, overflow: "hidden", background: "var(--bg-elev-2)" } },
        segs.filter((s) => s.value > 0).map((s) => R.createElement("div", {
          key: s.label, title: s.label + " · " + fmtUSD(s.value) + " (" + pct1(s.share) + ")" + (onSeg ? " — click to view these invoices" : ""),
          onClick: onSeg ? () => onSeg(s.label) : undefined,
          style: { width: pct1(s.share), background: s.color, cursor: onSeg ? "pointer" : "default" },
        }))),
      R.createElement("div", { style: { display: "flex", flexWrap: "wrap", gap: 10, marginTop: 8 } },
        segs.map((s) => R.createElement("span", { key: s.label, style: { fontSize: 10.5, color: "var(--text-3)", display: "inline-flex", alignItems: "center", gap: 4 } },
          R.createElement("span", { style: { width: 8, height: 8, borderRadius: 2, background: s.color, display: "inline-block" } }),
          s.label + " " + pct1(s.share)))),
      stressed
        ? R.createElement("div", { style: { marginTop: 8, fontSize: 11.5, fontWeight: 600, color: "var(--danger,#c0392b)" } },
            "⚠ " + oldest.label + " is " + pct1(oldest.share) + " of " + label + " — above the 5% stress line")
        : null);
  }

  function ConcentrationHealthPanel({ data, signals, setPage, setDrillKey }) {
    const analytics = (data && data.analytics) || {};
    const aging = (data && data.aging) || {};
    const Bar = window.BarRanking;

    const custHhi = sigBy(signals, ["customer_concentration_hhi", "revenue_concentration_hhi"]);
    const venHhi = sigBy(signals, ["vendor_concentration_hhi"]);
    const dpo = sigBy(signals, ["dpo_days"]);

    const drillCustomer = (r) => { if (window.__perduraDrillDataRoom) window.__perduraDrillDataRoom({ t: "ar_invoices", col: "customer_name", codes: [r.name], label: "Invoices · " + r.name }); };
    const drillVendor = (r) => { if (window.__perduraDrillDataRoom) window.__perduraDrillDataRoom({ t: "ap_bills", col: "vendor_name", codes: [r.name], label: "Bills · " + r.name }); };
    const goArap = (key) => { if (setDrillKey) setDrillKey(key); if (setPage) setPage("arap"); };
    // Aging segment → AR/AP detail, pre-filtered to that bucket for AR (AP opens unfiltered).
    const goAging = (sideKey, bucketLabel) => {
      try { window.__perduraArapBucket = sideKey === "ar" ? (AR_BUCKET_MAP[bucketLabel] || "all") : "all"; } catch (_e) { /* hint is best-effort */ }
      goArap(sideKey);
    };
    const linkBtn = (txt, onClick) => R.createElement("button", { className: "pc-btn-mini ghost", style: { cursor: "pointer", alignSelf: "flex-start", marginTop: 2 }, onClick }, txt);

    const custRows = (((analytics.topCustomers || {}).rows) || []).slice(0, 5).map((r) => ({ label: r.name, value: r.total, share: r.share_of_revenue, trend: r.trend, onClick: () => drillCustomer(r) }));
    const venRows = (((analytics.topVendors || {}).rows) || []).slice(0, 5).map((r) => ({ label: r.name, value: r.total, share: r.share_of_spend, trend: r.trend, onClick: () => drillVendor(r) }));

    const dpoStress = dpo && dpo.value != null && dpo.benchmark && dpo.benchmark.p50 != null && dpo.value > dpo.benchmark.p50;

    const col = (children) => R.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 16 } }, children);

    const left = col([
      R.createElement("div", { key: "c-top" }, subhead("Top customers · share of revenue"),
        Bar ? R.createElement(Bar, { rows: custRows, valueFmt: fmtUSD, maxRows: 5, emptyLabel: "Awaiting AR subledger (customers)" }) : null),
      R.createElement("div", { key: "c-hhi" }, subhead("Customer concentration"), R.createElement(HhiRow, { signal: custHhi })),
      R.createElement("div", { key: "c-aging", style: { display: "flex", flexDirection: "column", gap: 6 } }, subhead("AR aging"),
        R.createElement(AgingBar, { side: aging.ar, label: "AR", onSeg: (lbl) => goAging("ar", lbl) }),
        (aging.ar && aging.ar.hasSubledger) ? linkBtn("View AR detail →", () => goArap("ar")) : null),
    ]);

    const right = col([
      R.createElement("div", { key: "v-top" }, subhead("Top vendors · share of spend"),
        Bar ? R.createElement(Bar, { rows: venRows, valueFmt: fmtUSD, maxRows: 5, emptyLabel: "Awaiting AP subledger (vendors)" }) : null),
      R.createElement("div", { key: "v-hhi" }, subhead("Vendor concentration"), R.createElement(HhiRow, { signal: venHhi })),
      R.createElement("div", { key: "v-aging", style: { display: "flex", flexDirection: "column", gap: 6 } }, subhead("AP aging"),
        R.createElement(AgingBar, { side: aging.ap, label: "AP", onSeg: (lbl) => goAging("ap", lbl) }),
        dpoStress
          ? R.createElement("div", { style: { fontSize: 11.5, fontWeight: 600, color: "var(--warning,#b7791f)" } },
              "⚠ DPO " + Math.round(dpo.value) + "d is above the industry median (" + Math.round(dpo.benchmark.p50) + "d) — confirm intentional terms, not stretched payables")
          : null,
        (aging.ap && aging.ap.hasSubledger) ? linkBtn("View AP detail →", () => goArap("ap")) : null),
    ]);

    return R.createElement("div", { className: "pc-card", style: { padding: 0 } },
      R.createElement("div", { className: "pc-card-hd" },
        R.createElement("div", null,
          R.createElement("div", { className: "pc-card-title" }, "Concentration & Health"),
          R.createElement("div", { className: "pc-card-subtitle" }, "How dependent you are on top customers and vendors, and how aged your receivables and payables are"))),
      R.createElement("div", { className: "pc-grid-2", style: { padding: "14px 16px" } }, left, right));
  }

  window.ConcentrationHealthPanel = ConcentrationHealthPanel;
})();
