/* global React */
/* Charts: Sparkline, Line, Bar, Area, KPI metrics — all SVG, no deps */

const { useMemo, useState, useRef, useEffect } = React;

function useMeasure() {
  const ref = useRef(null);
  const [size, setSize] = useState({ w: 0, h: 0 });
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver((entries) => {
      for (const e of entries) {
        const cr = e.contentRect;
        setSize({ w: Math.round(cr.width), h: Math.round(cr.height) });
      }
    });
    ro.observe(ref.current);
    return () => ro.disconnect();
  }, []);
  return [ref, size];
}

function _scale(values, w, h, pad = 0) {
  const min = Math.min(...values);
  const max = Math.max(...values);
  const r = max - min || 1;
  return values.map((v, i) => [
    (i / (values.length - 1)) * (w - pad * 2) + pad,
    h - pad - ((v - min) / r) * (h - pad * 2),
  ]);
}

function Sparkline({ data, w = 120, h = 36, color = "currentColor", area = true, stroke = 1.5 }) {
  const pts = useMemo(() => _scale(data, w, h, 2), [data, w, h]);
  const d = pts.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(1)},${y.toFixed(1)}`).join(" ");
  const areaD = `${d} L${w - 2},${h - 2} L2,${h - 2} Z`;
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} style={{ display: "block" }}>
      {area && <path d={areaD} fill={color} opacity="0.08"/>}
      <path d={d} fill="none" stroke={color} strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

/* ---------- LineChart ---------- */
function LineChart({ series = [], labels = [], w: wIn = 720, h: hIn = 280, gradient = true, yTicks = 4, fill = false }) {
  const [ref, size] = useMeasure();
  const w = fill && size.w ? size.w : wIn;
  const h = fill && size.h ? size.h : hIn;
  const pad = { t: 16, r: 16, b: 28, l: 40 };
  const cw = w - pad.l - pad.r;
  const ch = h - pad.t - pad.b;
  const all = series.flatMap(s => s.data);
  const min = Math.min(0, ...all);
  const max = Math.max(...all);
  const r = max - min || 1;
  const x = (i, n) => pad.l + (i / (n - 1)) * cw;
  const y = (v) => pad.t + ch - ((v - min) / r) * ch;

  const ticks = useMemo(() => {
    const arr = [];
    for (let i = 0; i <= yTicks; i++) {
      const v = min + (r * i / yTicks);
      arr.push({ y: y(v), v });
    }
    return arr;
  }, [min, max, ch, yTicks]);

  const colors = ["var(--ink)", "var(--grad-4)", "var(--grad-1)", "var(--grad-2)"];

  const svg = (
    <svg width={fill ? "100%" : w} height={fill ? "100%" : h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: "block" }}>
      <defs>
        <linearGradient id="lineGrad" x1="0" x2="1" y1="0" y2="0">
          <stop offset="0%" stopColor="#2ABFB5"/>
          <stop offset="35%" stopColor="#0E8FBF"/>
          <stop offset="70%" stopColor="#6E2FA5"/>
          <stop offset="100%" stopColor="#D3278B"/>
        </linearGradient>
        <linearGradient id="lineGradFill" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="#6E2FA5" stopOpacity="0.18"/>
          <stop offset="100%" stopColor="#6E2FA5" stopOpacity="0"/>
        </linearGradient>
      </defs>

      {/* y grid */}
      {ticks.map((t, i) => (
        <g key={i}>
          <line x1={pad.l} x2={w - pad.r} y1={t.y} y2={t.y} stroke="var(--line)" strokeWidth="1"/>
          <text x={pad.l - 8} y={t.y + 4} fill="var(--ink-3)" fontSize="10" fontFamily="var(--mono)" textAnchor="end" letterSpacing="0.06em">
            {t.v >= 1000 ? (t.v / 1000).toFixed(0) + "K" : Math.round(t.v)}
          </text>
        </g>
      ))}

      {/* x labels */}
      {labels.map((l, i) => (
        <text key={i} x={x(i, labels.length)} y={h - 8} fill="var(--ink-3)" fontSize="10" fontFamily="var(--mono)" textAnchor="middle" letterSpacing="0.06em">
          {l}
        </text>
      ))}

      {/* series */}
      {series.map((s, si) => {
        const pts = s.data.map((v, i) => [x(i, s.data.length), y(v)]);
        const d = pts.map(([px, py], i) => `${i === 0 ? "M" : "L"}${px.toFixed(1)},${py.toFixed(1)}`).join(" ");
        const fillD = `${d} L${pts[pts.length - 1][0].toFixed(1)},${pad.t + ch} L${pts[0][0].toFixed(1)},${pad.t + ch} Z`;
        const stroke = si === 0 && gradient ? "url(#lineGrad)" : (s.color || colors[si % colors.length]);
        return (
          <g key={si}>
            {si === 0 && gradient && <path d={fillD} fill="url(#lineGradFill)"/>}
            <path d={d} fill="none" stroke={stroke} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" strokeDasharray={s.dashed ? "4 4" : undefined}/>
          </g>
        );
      })}
    </svg>
  );

  if (!fill) return svg;
  return <div ref={ref} style={{ width: "100%", height: "100%", minHeight: 0 }}>{size.w ? svg : null}</div>;
}

/* ---------- BarChart ---------- */
function BarChart({ data = [], labels = [], w: wIn = 720, h: hIn = 240, color = "var(--ink)", stacked, fill = false }) {
  const [ref, size] = useMeasure();
  const w = fill && size.w ? size.w : wIn;
  const h = fill && size.h ? size.h : hIn;
  const pad = { t: 12, r: 12, b: 28, l: 36 };
  const cw = w - pad.l - pad.r;
  const ch = h - pad.t - pad.b;
  const max = Math.max(...data.flat());
  const groupW = cw / data.length;
  const barW = Math.min(groupW * 0.55, 32);

  const svg = (
    <svg width={fill ? "100%" : w} height={fill ? "100%" : h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: "block" }}>
      <defs>
        <linearGradient id="barGrad" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="#D3278B"/>
          <stop offset="100%" stopColor="#6E2FA5"/>
        </linearGradient>
      </defs>
      {/* baseline */}
      <line x1={pad.l} x2={w - pad.r} y1={pad.t + ch} y2={pad.t + ch} stroke="var(--line-2)"/>
      {data.map((v, i) => {
        const cx = pad.l + groupW * i + groupW / 2;
        const bh = (v / max) * ch;
        const isLast = i === data.length - 1;
        return (
          <g key={i}>
            <rect
              x={cx - barW / 2} y={pad.t + ch - bh}
              width={barW} height={bh}
              fill={isLast ? "url(#barGrad)" : color}
              opacity={isLast ? 1 : 0.85}
            />
            <text x={cx} y={h - 8} fill="var(--ink-3)" fontSize="10" fontFamily="var(--mono)" textAnchor="middle" letterSpacing="0.06em">
              {labels[i]}
            </text>
          </g>
        );
      })}
    </svg>
  );

  if (!fill) return svg;
  return <div ref={ref} style={{ width: "100%", height: "100%", minHeight: 0 }}>{size.w ? svg : null}</div>;
}

/* ---------- AreaChart ---------- */
function AreaChart({ data = [], labels = [], w = 720, h = 200 }) {
  const pad = { t: 8, r: 8, b: 24, l: 36 };
  const cw = w - pad.l - pad.r;
  const ch = h - pad.t - pad.b;
  const max = Math.max(...data);
  const min = Math.min(0, ...data);
  const r = max - min || 1;
  const pts = data.map((v, i) => [
    pad.l + (i / (data.length - 1)) * cw,
    pad.t + ch - ((v - min) / r) * ch,
  ]);
  const d = pts.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(1)},${y.toFixed(1)}`).join(" ");
  const fillD = `${d} L${pad.l + cw},${pad.t + ch} L${pad.l},${pad.t + ch} Z`;
  return (
    <svg width="100%" height={h} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ display: "block" }}>
      <defs>
        <linearGradient id="areaFill" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor="#0E8FBF" stopOpacity="0.32"/>
          <stop offset="100%" stopColor="#0E8FBF" stopOpacity="0"/>
        </linearGradient>
      </defs>
      <path d={fillD} fill="url(#areaFill)"/>
      <path d={d} fill="none" stroke="#0E8FBF" strokeWidth="1.75" strokeLinejoin="round" strokeLinecap="round"/>
      {labels.map((l, i) => (
        <text key={i} x={pad.l + (i / (data.length - 1)) * cw} y={h - 6} fill="var(--ink-3)" fontSize="10" fontFamily="var(--mono)" textAnchor="middle" letterSpacing="0.06em">
          {i % Math.ceil(labels.length / 6) === 0 ? l : ""}
        </text>
      ))}
    </svg>
  );
}

/* ---------- KPI Card ---------- */
function KPICard({ label, value, delta, deltaLabel = "vs last 7d", spark, gradient, big }) {
  const positive = delta != null && delta >= 0;
  return (
    <div className="card" style={{ position: "relative", overflow: "hidden", padding: "var(--d-card-p)" }}>
      <div className="row between" style={{ alignItems: "flex-start", gap: 8, marginBottom: 14 }}>
        <span className="mono" style={{ color: "var(--ink-3)" }}>{label}</span>
        {delta != null && (
          <span className="row gap-4" style={{
            fontFamily: "var(--mono)", fontSize: 11, fontWeight: 700, letterSpacing: "0.06em",
            color: positive ? "var(--pos)" : "var(--neg)",
          }}>
            {positive ? "▲" : "▼"} {Math.abs(delta).toFixed(1)}%
          </span>
        )}
      </div>
      <div style={{
        fontFamily: "var(--sans)", fontWeight: 600,
        fontSize: big ? "clamp(48px, 5vw, 72px)" : "clamp(36px, 3.6vw, 48px)",
        letterSpacing: "-0.035em", lineHeight: 0.95,
      }}>
        {gradient ? <span className="grad-text">{value}</span> : <span>{value}</span>}
      </div>
      {(spark || deltaLabel) && (
        <div className="row between" style={{ marginTop: 16, alignItems: "flex-end", gap: 12 }}>
          {deltaLabel && <span className="mono" style={{ color: "var(--ink-3)" }}>{deltaLabel}</span>}
          {spark && <Sparkline data={spark} w={90} h={28} color={positive ? "var(--pos)" : "var(--neg)"} area={false}/>}
        </div>
      )}
    </div>
  );
}

/* ---------- Stat Block (editorial, bordered top) ---------- */
function StatBlock({ label, value, sub, gradient }) {
  return (
    <div style={{ borderTop: "1px solid var(--ink)", paddingTop: 14 }}>
      <div style={{
        fontFamily: "var(--sans)", fontWeight: 600,
        fontSize: "clamp(48px, 5.5vw, 80px)",
        letterSpacing: "-0.04em", lineHeight: 0.95,
      }}>
        {gradient ? <span className="grad-text">{value}</span> : value}
      </div>
      <div className="mono" style={{ marginTop: 10, color: "var(--ink-2)" }}>{label}</div>
      {sub && <div style={{ marginTop: 6, fontSize: 13, color: "var(--ink-3)" }}>{sub}</div>}
    </div>
  );
}

/* ---------- Donut ---------- */
function Donut({ value = 60, size = 120, stroke = 12, color = "var(--brand-grad)", label, sub }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const off = c - (value / 100) * c;
  return (
    <div style={{ position: "relative", width: size, height: size }}>
      <svg width={size} height={size}>
        <defs>
          <linearGradient id="donutG" x1="0" x2="1" y1="0" y2="1">
            <stop offset="0%" stopColor="#2ABFB5"/>
            <stop offset="50%" stopColor="#6E2FA5"/>
            <stop offset="100%" stopColor="#D3278B"/>
          </linearGradient>
        </defs>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--line)" strokeWidth={stroke}/>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="url(#donutG)" strokeWidth={stroke}
          strokeDasharray={c} strokeDashoffset={off}
          strokeLinecap="butt" transform={`rotate(-90 ${size/2} ${size/2})`}/>
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>
        <span style={{ fontWeight: 600, fontSize: 22, letterSpacing: "-0.02em" }}>{label || `${value}%`}</span>
        {sub && <span className="mono" style={{ color: "var(--ink-3)", marginTop: 2 }}>{sub}</span>}
      </div>
    </div>
  );
}

Object.assign(window, { Sparkline, LineChart, BarChart, AreaChart, KPICard, StatBlock, Donut });
