// Perdura — reusable dashboard chart primitives (Stage 2.A).
//
// Lightweight, dependency-free SVG components matching the house style of
// src/widgets.jsx (no external chart lib — Chart.js/D3 would be heavier than the
// need). Neutral palette, minimal animation. Attached to window for the
// text/babel page scripts to consume:
//   window.SparklineMini, window.BarRanking, window.TrendLine

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

  const NEUTRAL = "var(--accent, #6366f1)";
  const fmtMoney = (n) => (window.fmtUSD ? window.fmtUSD(n, { compact: true }) : String(n));

  // ── SparklineMini ─────────────────────────────────────────────────────────
  // A compact trend line (no fill by default). Honest-empty: renders nothing
  // when there are < 2 finite points, so a tile never shows a fake flat line.
  function SparklineMini({ values, width = 72, height = 22, color = NEUTRAL, strokeWidth = 1.5, fill = false }) {
    const vals = (values || []).map(Number).filter((v) => Number.isFinite(v));
    if (vals.length < 2) return null;
    const min = Math.min(...vals), max = Math.max(...vals);
    const range = max - min || 1;
    const step = width / (vals.length - 1);
    const pts = vals.map((v, i) => [i * step, height - ((v - min) / range) * (height - 2) - 1]);
    const path = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(1) + "," + p[1].toFixed(1)).join(" ");
    const last = pts[pts.length - 1];
    return h("svg", { width, height, style: { display: "block", overflow: "visible" } },
      fill ? h("path", { d: path + ` L${width},${height} L0,${height} Z`, fill: color, fillOpacity: 0.10 }) : null,
      h("path", { d: path, fill: "none", stroke: color, strokeWidth, strokeLinecap: "round", strokeLinejoin: "round" }),
      h("circle", { cx: last[0], cy: last[1], r: 1.8, fill: color }),
    );
  }

  // ── BarRanking ────────────────────────────────────────────────────────────
  // Horizontal ranked bars for top-N panels (customers, vendors, …).
  // rows: [{ label, value, share?, trend?('up'|'down'|'flat'), onClick? }]
  // Each row is whole-row clickable when onClick (or rowOnClick) is provided.
  function BarRanking({ rows, color = NEUTRAL, valueFmt = fmtMoney, maxRows = 10, emptyLabel }) {
    const data = (rows || []).slice(0, maxRows);
    if (!data.length) {
      return h("div", { style: { padding: "16px 4px", fontSize: 12.5, color: "var(--text-3)" } },
        emptyLabel || "No rows to show.");
    }
    const max = Math.max.apply(null, data.map((r) => Math.abs(Number(r.value) || 0))) || 1;
    const trendGlyph = { up: "▲", down: "▼", flat: "—" };
    const trendColor = { up: "var(--positive, #10b981)", down: "var(--danger, #ef4444)", flat: "var(--text-3)" };
    return h("div", { style: { display: "flex", flexDirection: "column", gap: 7 } },
      data.map((r, i) => {
        const pct = Math.max(2, (Math.abs(Number(r.value) || 0) / max) * 100);
        const click = r.onClick;
        return h("div", {
          key: i,
          onClick: click || undefined,
          style: {
            cursor: click ? "pointer" : "default",
            display: "grid", gridTemplateColumns: "minmax(90px, 1.4fr) 2fr auto", gap: 10,
            alignItems: "center", padding: "5px 8px", borderRadius: 7,
            transition: "background 0.12s",
          },
          onMouseEnter: click ? (e) => (e.currentTarget.style.background = "var(--bg-elev-1)") : undefined,
          onMouseLeave: click ? (e) => (e.currentTarget.style.background = "transparent") : undefined,
          title: click ? "Drill into this in the Data Room" : undefined,
        },
          h("div", { style: { fontSize: 12.5, color: "var(--text-1, #111)", fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } },
            r.label,
            r.sub ? h("span", { style: { color: "var(--text-3)", fontWeight: 400 } }, "  " + r.sub) : null),
          h("div", { style: { height: 8, background: "var(--bg-elev-2, #eee)", borderRadius: 4, overflow: "hidden" } },
            h("div", { style: { width: pct + "%", height: "100%", background: color, borderRadius: 4 } })),
          h("div", { style: { display: "flex", alignItems: "center", gap: 8, justifyContent: "flex-end", whiteSpace: "nowrap" } },
            r.share != null ? h("span", { style: { fontSize: 11, color: "var(--text-3)" } }, (r.share * 100).toFixed(1) + "%") : null,
            h("span", { style: { fontSize: 12.5, fontFamily: "ui-monospace, monospace", color: "var(--text-1, #111)" } }, valueFmt(r.value)),
            r.trend ? h("span", { style: { fontSize: 10, color: trendColor[r.trend] || "var(--text-3)" } }, trendGlyph[r.trend] || "") : null),
        );
      }),
    );
  }

  // ── TrendLine ─────────────────────────────────────────────────────────────
  // A clean multi-point line with an optional baseline. Larger than the mini
  // sparkline; used in hero tiles / detail cards. Honest-empty when < 2 points.
  function TrendLine({ values, height = 48, color = NEUTRAL, strokeWidth = 2, fill = true }) {
    const vals = (values || []).map(Number).filter((v) => Number.isFinite(v));
    if (vals.length < 2) {
      return h("div", { style: { height, display: "flex", alignItems: "center", fontSize: 11, color: "var(--text-3)" } }, "Not enough history");
    }
    const W = 100; // viewBox width (scales to container)
    const min = Math.min(...vals), max = Math.max(...vals);
    const range = max - min || 1;
    const step = W / (vals.length - 1);
    const pts = vals.map((v, i) => [i * step, height - ((v - min) / range) * (height - 4) - 2]);
    const path = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(1) + "," + p[1].toFixed(1)).join(" ");
    return h("svg", { viewBox: `0 0 ${W} ${height}`, preserveAspectRatio: "none", width: "100%", height, style: { display: "block" } },
      fill ? h("path", { d: path + ` L${W},${height} L0,${height} Z`, fill: color, fillOpacity: 0.08 }) : null,
      h("path", { d: path, fill: "none", stroke: color, strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", vectorEffect: "non-scaling-stroke" }),
    );
  }

  window.SparklineMini = SparklineMini;
  window.BarRanking = BarRanking;
  window.TrendLine = TrendLine;
})();
