/* ============================================================
   charts.jsx — quiet, precise SVG charts.
   Every series chart shows the personal-baseline / SWC band.
   No gradients, no chartjunk. Status lives in the dot, not the line.
   Exports to window.
   ============================================================ */
(function () {
  const { useId } = React;

  // map helpers
  const lerp = (a, b, t) => a + (b - a) * t;
  function extent(arr, pad = 0) {
    let lo = Math.min(...arr), hi = Math.max(...arr);
    if (lo === hi) { lo -= 1; hi += 1; }
    const p = (hi - lo) * pad;
    return [lo - p, hi + p];
  }
  const STATUS_VAR = {
    good: "var(--good)", attn: "var(--attn)", within: "var(--c-line)",
    lowconf: "var(--ink-4)", acute: "var(--acute)",
  };

  // ---------------------------------------------------------
  // Spark — sparkline with baseline + SWC band + status dot
  // ---------------------------------------------------------
  function Spark({ data, w = 132, h = 40, baseline, swc, status = "within", lowconf = false, dot = true, pad = 6 }) {
    const all = data.concat(
      baseline != null ? [baseline - (swc || 0) * 1.4, baseline + (swc || 0) * 1.4] : []
    );
    const [lo, hi] = extent(all, 0.12);
    const x = (i) => lerp(pad, w - pad, data.length < 2 ? 0.5 : i / (data.length - 1));
    const y = (v) => lerp(h - pad, pad, (v - lo) / (hi - lo));
    const pts = data.map((v, i) => `${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
    const last = data[data.length - 1];
    const col = STATUS_VAR[status] || STATUS_VAR.within;
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width={w} height={h} style={{ display: "block", overflow: "visible" }}>
        {baseline != null && swc != null && (
          <rect x={pad - 2} y={y(baseline + swc)} width={w - pad * 2 + 4}
            height={Math.max(1, y(baseline - swc) - y(baseline + swc))}
            fill="var(--c-band)" />
        )}
        {baseline != null && (
          <line x1={pad - 2} x2={w - pad + 2} y1={y(baseline)} y2={y(baseline)}
            stroke="var(--c-baseline)" strokeWidth="1" strokeDasharray="2 2.5" />
        )}
        <polyline points={pts} fill="none"
          stroke={lowconf ? "var(--ink-4)" : "var(--c-line)"}
          strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="round"
          strokeDasharray={lowconf ? "3 2.5" : "none"} opacity={lowconf ? 0.8 : 1} />
        {dot && (
          <>
            <circle cx={x(data.length - 1)} cy={y(last)} r="3.6" fill="var(--surface)" />
            <circle cx={x(data.length - 1)} cy={y(last)} r="2.7" fill={col} />
          </>
        )}
      </svg>
    );
  }

  // ---------------------------------------------------------
  // SignalSpark — tiny directional spark for sub-acute signals
  // ---------------------------------------------------------
  function SignalSpark({ data, dir, higherBetter, w = 64, h = 26 }) {
    const [lo, hi] = extent(data, 0.15);
    const x = (i) => lerp(2, w - 2, i / (data.length - 1));
    const y = (v) => lerp(h - 3, 3, (v - lo) / (hi - lo));
    const pts = data.map((v, i) => `${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
    const bad = (dir === "up") !== higherBetter; // moving in the unhelpful direction
    const col = bad ? "var(--attn)" : "var(--good)";
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width={w} height={h} style={{ display: "block", overflow: "visible" }}>
        <polyline points={pts} fill="none" stroke={col} strokeWidth="1.5"
          strokeLinejoin="round" strokeLinecap="round" />
        <circle cx={x(data.length - 1)} cy={y(data[data.length - 1])} r="2.4" fill={col} />
      </svg>
    );
  }

  // ---------------------------------------------------------
  // TrendChart — long range line + 7d rolling avg + SWC band
  // ---------------------------------------------------------
  function TrendChart({ raw, avg, baseline, swc, w = 320, h = 132, unit = "", label = "", threshold = null, status = "within", higherBetter = true }) {
    const padL = 8, padR = 30, padT = 10, padB = 18;
    const all = raw.concat(baseline != null ? [baseline - swc, baseline + swc] : [], threshold ? [threshold.value] : []);
    const [lo, hi] = extent(all, 0.08);
    const x = (i) => lerp(padL, w - padR, i / (raw.length - 1));
    const y = (v) => lerp(h - padB, padT, (v - lo) / (hi - lo));
    const rawPts = raw.map((v, i) => `${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
    const avgPts = avg.map((v, i) => `${x(i).toFixed(1)},${y(v).toFixed(1)}`).join(" ");
    const ticks = [lo, (lo + hi) / 2, hi];
    const col = STATUS_VAR[status];
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none" style={{ display: "block", overflow: "visible" }}>
        {ticks.map((t, i) => (
          <g key={i}>
            <line x1={padL} x2={w - padR} y1={y(t)} y2={y(t)} stroke="var(--c-grid)" strokeWidth="1" />
            <text x={w - padR + 4} y={y(t) + 3} fontFamily="var(--font-mono)" fontSize="9" fill="var(--c-axis)">{t.toFixed(t < 10 ? 1 : 0)}</text>
          </g>
        ))}
        {baseline != null && (
          <rect x={padL} y={y(baseline + swc)} width={w - padR - padL}
            height={Math.max(1, y(baseline - swc) - y(baseline + swc))} fill="var(--c-band)" />
        )}
        {baseline != null && (
          <line x1={padL} x2={w - padR} y1={y(baseline)} y2={y(baseline)} stroke="var(--c-baseline)" strokeWidth="1" strokeDasharray="3 3" />
        )}
        {threshold && (
          <g>
            <line x1={padL} x2={w - padR} y1={y(threshold.value)} y2={y(threshold.value)} stroke="var(--c-threshold)" strokeWidth="1" strokeDasharray="1 3" />
            <text x={padL + 2} y={y(threshold.value) - 3} fontFamily="var(--font-mono)" fontSize="8.5" fill="var(--attn)">{threshold.label}</text>
          </g>
        )}
        <polyline points={rawPts} fill="none" stroke="var(--c-ghost)" strokeWidth="1" strokeLinejoin="round" />
        <polyline points={avgPts} fill="none" stroke="var(--c-line)" strokeWidth="1.8" strokeLinejoin="round" strokeLinecap="round" />
        <circle cx={x(raw.length - 1)} cy={y(avg[avg.length - 1])} r="3" fill="var(--surface)" />
        <circle cx={x(raw.length - 1)} cy={y(avg[avg.length - 1])} r="2.3" fill={col} />
      </svg>
    );
  }

  // ---------------------------------------------------------
  // FFFChart — fitness / fatigue / form
  // ---------------------------------------------------------
  function FFF({ ctl, atl, form, w = 320, h = 150 }) {
    const padL = 8, padR = 8, padT = 10, padB = 20;
    const allTop = ctl.concat(atl);
    const [lo, hi] = extent(allTop, 0.08);
    const fext = extent(form, 0.2);
    const x = (i) => lerp(padL, w - padR, i / (ctl.length - 1));
    const yTop = (v) => lerp(h - padB, padT, (v - lo) / (hi - lo));
    const line = (arr) => arr.map((v, i) => `${x(i).toFixed(1)},${yTop(v).toFixed(1)}`).join(" ");
    // form as zero-baselined area at bottom band
    const fy = (v) => lerp(h - padB, h - padB - 34, (v - fext[0]) / (fext[1] - fext[0]));
    const zeroY = fy(0);
    const formArea = form.map((v, i) => `${x(i).toFixed(1)},${fy(v).toFixed(1)}`).join(" ");
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none" style={{ display: "block", overflow: "visible" }}>
        <line x1={padL} x2={w - padR} y1={zeroY} y2={zeroY} stroke="var(--c-grid)" strokeWidth="1" strokeDasharray="3 3" />
        <polyline points={line(ctl)} fill="none" stroke="var(--c-line)" strokeWidth="1.8" />
        <polyline points={line(atl)} fill="none" stroke="var(--attn)" strokeWidth="1.4" strokeDasharray="4 3" opacity="0.85" />
        <polyline points={formArea} fill="none" stroke="var(--good)" strokeWidth="1.4" />
        <circle cx={x(ctl.length - 1)} cy={yTop(ctl[ctl.length - 1])} r="2.4" fill="var(--c-line)" />
      </svg>
    );
  }

  // ---------------------------------------------------------
  // Circadian — hourly activity bars, M10 / L5 windows marked
  // ---------------------------------------------------------
  function Circadian({ data, w = 320, h = 110 }) {
    const padB = 16, padT = 8, padX = 6;
    const hi = Math.max(...data);
    const bw = (w - padX * 2) / 24;
    // M10 = most-active 10h window; L5 = least-active 5h
    function bestWindow(len, most) {
      let bi = 0, bv = most ? -Infinity : Infinity;
      for (let i = 0; i + len <= 24; i++) {
        const s = data.slice(i, i + len).reduce((a, b) => a + b, 0);
        if (most ? s > bv : s < bv) { bv = s; bi = i; }
      }
      return [bi, bi + len];
    }
    const m10 = bestWindow(10, true), l5 = bestWindow(5, false);
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none" style={{ display: "block" }}>
        <rect x={padX + m10[0] * bw} y={padT - 3} width={(m10[1] - m10[0]) * bw} height={h - padB - padT + 3} fill="var(--good-soft)" />
        <rect x={padX + l5[0] * bw} y={padT - 3} width={(l5[1] - l5[0]) * bw} height={h - padB - padT + 3} fill="var(--c-band)" />
        {data.map((v, i) => {
          const bh = Math.max(1, (v / hi) * (h - padB - padT));
          return <rect key={i} x={padX + i * bw + 0.6} y={h - padB - bh} width={bw - 1.2} height={bh} rx="0.5" fill="var(--ink-4)" />;
        })}
        {[0, 6, 12, 18, 24].map((hh) => (
          <text key={hh} x={padX + hh * bw} y={h - 4} fontFamily="var(--font-mono)" fontSize="8.5" fill="var(--c-axis)" textAnchor={hh === 24 ? "end" : hh === 0 ? "start" : "middle"}>{String(hh).padStart(2, "0")}</text>
        ))}
      </svg>
    );
  }

  // ---------------------------------------------------------
  // StageBar — sleep stages, de-emphasized (lower confidence)
  // ---------------------------------------------------------
  function StageBar({ stages, w = 320, h = 24 }) {
    if (!stages) return null;
    const order = [["deep", "Deep"], ["rem", "REM"], ["light", "Light"], ["awake", "Awake"]];
    const total = order.reduce((a, [k]) => a + stages[k], 0);
    let x = 0;
    const shades = { deep: "var(--ink-2)", rem: "var(--ink-3)", light: "var(--ink-4)", awake: "var(--line-strong)" };
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none" style={{ display: "block" }}>
        {order.map(([k]) => {
          const bw = (stages[k] / total) * w;
          const seg = <rect key={k} x={x} y={0} width={Math.max(0, bw - 1.5)} height={h} rx="2" fill={shades[k]} opacity="0.7" />;
          x += bw;
          return seg;
        })}
      </svg>
    );
  }

  // ---------------------------------------------------------
  // StrainBar — value within personal target band
  // ---------------------------------------------------------
  function StrainBar({ value, target, max = 21, w = 280, h = 30 }) {
    const x = (v) => (v / max) * w;
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none" style={{ display: "block", overflow: "visible" }}>
        <rect x="0" y={h / 2 - 3} width={w} height="6" rx="3" fill="var(--surface-2)" />
        <rect x={x(target[0])} y={h / 2 - 3} width={x(target[1]) - x(target[0])} height="6" rx="3" fill="var(--good-soft)" />
        {value != null && (
          <>
            <line x1={x(value)} x2={x(value)} y1="3" y2={h - 3} stroke="var(--ink)" strokeWidth="2" />
            <circle cx={x(value)} cy={h / 2} r="4.5" fill="var(--surface)" stroke="var(--ink)" strokeWidth="2" />
          </>
        )}
      </svg>
    );
  }

  // ---------------------------------------------------------
  // RecoveryDist (Variation A) — probability density of the score
  // Communicates uncertainty as a distribution. Wider = less sure.
  // ---------------------------------------------------------
  function RecoveryDist({ value, band, w = 300, h = 116, status = "within" }) {
    const padX = 6, padB = 20, top = 8;
    const sigma = band; // ±band ~ 1 sigma
    const x = (v) => lerp(padX, w - padX, v / 100);
    const g = (v) => Math.exp(-0.5 * Math.pow((v - value) / sigma, 2));
    const N = 120;
    const xs = Array.from({ length: N + 1 }, (_, i) => (i / N) * 100);
    const peak = 1;
    const yArea = h - padB;
    const yTop = top;
    const yy = (gv) => lerp(yArea, yTop, gv / peak);
    const curve = xs.map((v) => `${x(v).toFixed(1)},${yy(g(v)).toFixed(1)}`);
    const areaIn = xs.filter((v) => v >= value - band && v <= value + band);
    const inPath = `M ${x(value - band).toFixed(1)},${yArea} ` +
      areaIn.map((v) => `L ${x(v).toFixed(1)},${yy(g(v)).toFixed(1)}`).join(" ") +
      ` L ${x(value + band).toFixed(1)},${yArea} Z`;
    const fullArea = `M ${x(0)},${yArea} ` + curve.map((c) => `L ${c}`).join(" ") + ` L ${x(100)},${yArea} Z`;
    const col = STATUS_VAR[status];
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} style={{ display: "block", overflow: "visible" }}>
        {[0, 25, 50, 75, 100].map((t) => (
          <g key={t}>
            <line x1={x(t)} x2={x(t)} y1={top - 2} y2={yArea} stroke="var(--c-grid)" strokeWidth="1" opacity={t % 50 === 0 ? 1 : 0.5} />
            <text x={x(t)} y={h - 5} fontFamily="var(--font-mono)" fontSize="8.5" fill="var(--c-axis)" textAnchor={t === 0 ? "start" : t === 100 ? "end" : "middle"}>{t}</text>
          </g>
        ))}
        <path d={fullArea} fill="var(--c-band)" />
        <path d={inPath} fill={col} opacity="0.18" />
        <polyline points={curve.join(" ")} fill="none" stroke={col} strokeWidth="1.6" />
        <line x1={x(value)} x2={x(value)} y1={yy(g(value)) - 2} y2={yArea} stroke={col} strokeWidth="1.5" />
        <circle cx={x(value)} cy={yy(g(value)) - 2} r="3" fill={col} />
        {/* ± band ticks */}
        {[value - band, value + band].map((v, i) => (
          <line key={i} x1={x(v)} x2={x(v)} y1={yArea - 4} y2={yArea} stroke={col} strokeWidth="1.5" />
        ))}
      </svg>
    );
  }

  // ---------------------------------------------------------
  // RecoveryBand (Variation B) — 0–100 track + ± interval + typical marker
  // ---------------------------------------------------------
  function RecoveryBand({ value, band, typical = 64, w = 300, h = 56, status = "within" }) {
    const padX = 8, trackY = 26;
    const x = (v) => lerp(padX, w - padX, v / 100);
    const col = STATUS_VAR[status];
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} style={{ display: "block", overflow: "visible" }}>
        <rect x={padX} y={trackY - 4} width={w - padX * 2} height="8" rx="4" fill="var(--surface-2)" />
        {[0, 25, 50, 75, 100].map((t) => (
          <text key={t} x={x(t)} y={h - 4} fontFamily="var(--font-mono)" fontSize="8.5" fill="var(--c-axis)" textAnchor={t === 0 ? "start" : t === 100 ? "end" : "middle"}>{t}</text>
        ))}
        {/* personal typical recovery marker */}
        <line x1={x(typical)} x2={x(typical)} y1={trackY - 9} y2={trackY + 9} stroke="var(--c-baseline)" strokeWidth="1.2" strokeDasharray="2 2" />
        {/* ± interval */}
        <rect x={x(value - band)} y={trackY - 4} width={x(value + band) - x(value - band)} height="8" rx="4" fill={col} opacity="0.22" />
        <line x1={x(value - band)} x2={x(value - band)} y1={trackY - 7} y2={trackY + 7} stroke={col} strokeWidth="1.4" />
        <line x1={x(value + band)} x2={x(value + band)} y1={trackY - 7} y2={trackY + 7} stroke={col} strokeWidth="1.4" />
        <circle cx={x(value)} cy={trackY} r="5.5" fill="var(--surface)" stroke={col} strokeWidth="2.4" />
      </svg>
    );
  }

  // ---------------------------------------------------------
  // RecoveryArc (Variation C) — gauge with uncertainty arc
  // ---------------------------------------------------------
  function RecoveryArc({ value, band, w = 168, h = 110, status = "within" }) {
    const cx = w / 2, cy = h - 12, r = 70;
    const a0 = Math.PI, a1 = 0; // 180deg sweep
    const ang = (v) => lerp(a0, a1, v / 100);
    const pt = (v, rr = r) => [cx + rr * Math.cos(ang(v)), cy + rr * Math.sin(ang(v)) * -1];
    function arc(v0, v1, rr) {
      const [x0, y0] = pt(v0, rr), [x1, y1] = pt(v1, rr);
      const large = Math.abs(ang(v1) - ang(v0)) > Math.PI ? 1 : 0;
      return `M ${x0.toFixed(1)} ${y0.toFixed(1)} A ${rr} ${rr} 0 ${large} 1 ${x1.toFixed(1)} ${y1.toFixed(1)}`;
    }
    const col = STATUS_VAR[status];
    const [nx, ny] = pt(value);
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width={w} height={h} style={{ display: "block", overflow: "visible" }}>
        <path d={arc(0, 100, r)} fill="none" stroke="var(--surface-2)" strokeWidth="9" strokeLinecap="round" />
        <path d={arc(Math.max(0, value - band), Math.min(100, value + band), r)} fill="none" stroke={col} strokeWidth="9" strokeLinecap="round" opacity="0.28" />
        <path d={arc(0, value, r)} fill="none" stroke={col} strokeWidth="9" strokeLinecap="round" />
        <line x1={cx} y1={cy} x2={nx} y2={ny} stroke="var(--ink)" strokeWidth="2" />
        <circle cx={cx} cy={cy} r="3.5" fill="var(--ink)" />
      </svg>
    );
  }

  Object.assign(window, {
    Spark, SignalSpark, TrendChart, FFF, Circadian, StageBar, StrainBar,
    RecoveryDist, RecoveryBand, RecoveryArc,
  });
})();
