/* charts.jsx — lightweight responsive SVG charts with hover tooltips */
const { fmt } = window.RX;

function useWidth(ref) {
  const [w, setW] = useState(720);
  useEffect(() => {
    if (!ref.current) return;
    const ro = new ResizeObserver(es => { for (const e of es) setW(e.contentRect.width); });
    ro.observe(ref.current);
    setW(ref.current.clientWidth);
    return () => ro.disconnect();
  }, []);
  return w;
}

function niceMax(v) {
  if (v <= 0) return 10;
  const pow = Math.pow(10, Math.floor(Math.log10(v)));
  const n = v / pow;
  const step = n <= 1 ? 1 : n <= 2 ? 2 : n <= 2.5 ? 2.5 : n <= 5 ? 5 : 10;
  return step * pow;
}

/* ---------- Line / Area chart ---------- */
function AreaLine({ series, labels, height = 220, filled = true, showGrid = true, annotations = [], formatY = fmt, yFromZero = true }) {
  const ref = useRef();
  const W = useWidth(ref);
  const [hover, setHover] = useState(null); // {idx, cx, cy, clientX, clientY}
  const H = height, padL = 46, padR = 16, padT = 16, padB = 26;
  const n = labels.length;
  const allVals = series.flatMap(s => s.values);
  const rawMax = Math.max(1, ...allVals);
  const rawMin = yFromZero ? 0 : Math.min(...allVals);
  const yMax = niceMax(rawMax);
  const yMin = yFromZero ? 0 : Math.max(0, Math.floor(rawMin / niceMax(rawMax) * 4) / 4 * yMax - 0); // simple
  const lo = yFromZero ? 0 : Math.min(...allVals) * 0.96;
  const top = yMax, bot = yFromZero ? 0 : lo;
  const xOf = (i) => padL + (W - padL - padR) * (n === 1 ? 0.5 : i / (n - 1));
  const yOf = (v) => padT + (H - padT - padB) * (1 - (v - bot) / (top - bot || 1));
  const ticks = 4;
  const gridVals = Array.from({ length: ticks + 1 }, (_, i) => bot + (top - bot) * i / ticks);
  const labelEvery = Math.ceil(n / Math.max(6, Math.floor(W / 90)));

  const move = (e) => {
    const rect = ref.current.getBoundingClientRect();
    const x = e.clientX - rect.left;
    let idx = Math.round((x - padL) / ((W - padL - padR) / Math.max(1, n - 1)));
    idx = Math.max(0, Math.min(n - 1, idx));
    setHover({ idx, clientX: e.clientX, clientY: e.clientY });
  };

  return (
    <div ref={ref} style={{ position: "relative", width: "100%" }} onMouseLeave={() => setHover(null)}>
      <svg width={W} height={H} style={{ display: "block" }} onMouseMove={move}>
        {showGrid && gridVals.map((g, i) => (
          <g key={i}>
            <line x1={padL} x2={W - padR} y1={yOf(g)} y2={yOf(g)} stroke="var(--grid)" strokeWidth="1" />
            <text x={padL - 8} y={yOf(g) + 3.5} textAnchor="end" fontSize="10.5" fill="var(--text-3)"
                  fontFamily="Geist Mono, monospace">{formatY(g)}</text>
          </g>
        ))}
        {annotations.map((a, i) => (
          <g key={"a" + i}>
            <line x1={xOf(a.idx)} x2={xOf(a.idx)} y1={padT} y2={H - padB} stroke="var(--warn)" strokeWidth="1.2" strokeDasharray="3 3" opacity="0.8" />
            <rect x={Math.min(xOf(a.idx) + 6, W - 150)} y={padT + 1} width="142" height="28" rx="6" fill="var(--panel-2)" stroke="var(--border)" />
            <text x={Math.min(xOf(a.idx) + 13, W - 143)} y={padT + 12} fontSize="9.5" fill="var(--warn)" fontWeight="600">{a.label[0]}</text>
            <text x={Math.min(xOf(a.idx) + 13, W - 143)} y={padT + 23} fontSize="9.5" fill="var(--text-3)">{a.label[1]}</text>
          </g>
        ))}
        {/* x labels */}
        {labels.map((l, i) => (i % labelEvery === 0 || i === n - 1) &&
          <text key={"x" + i} x={xOf(i)} y={H - 8} textAnchor="middle" fontSize="10.5" fill="var(--text-3)" fontFamily="Geist Mono, monospace">{l}</text>)}
        {/* series */}
        {series.map((s, si) => {
          const pts = s.values.map((v, i) => [xOf(i), yOf(v)]);
          const line = pts.map((p, i) => (i ? "L" : "M") + p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" ");
          const area = line + ` L${xOf(n - 1).toFixed(1)} ${yOf(bot).toFixed(1)} L${xOf(0).toFixed(1)} ${yOf(bot).toFixed(1)} Z`;
          return (
            <g key={si}>
              {filled && !s.dashed &&
                <path d={area} fill={s.color} opacity={si === 0 ? 0.13 : 0.07} />}
              <path d={line} fill="none" stroke={s.color} strokeWidth="2"
                    strokeDasharray={s.dashed ? "5 4" : "0"} strokeLinejoin="round" strokeLinecap="round"
                    opacity={s.dashed ? 0.7 : 1} />
            </g>
          );
        })}
        {/* hover */}
        {hover && <>
          <line x1={xOf(hover.idx)} x2={xOf(hover.idx)} y1={padT} y2={H - padB} stroke="var(--border-strong)" strokeWidth="1" />
          {series.map((s, si) => (
            <circle key={si} cx={xOf(hover.idx)} cy={yOf(s.values[hover.idx])} r="3.5" fill="var(--panel)" stroke={s.color} strokeWidth="2" />
          ))}
        </>}
      </svg>
      {hover &&
        <ChartTip clientX={hover.clientX} clientY={hover.clientY} title={labels[hover.idx]}
          rows={series.map(s => ({ name: s.label, color: s.color, value: formatY(s.values[hover.idx]) }))} />}
    </div>
  );
}

/* ---------- Vertical bars (forecast) ---------- */
function VBars({ data, height = 220, color = "var(--accent)", formatY = fmt }) {
  // data: [{label, value, sub, faint}]
  const ref = useRef();
  const W = useWidth(ref);
  const [hover, setHover] = useState(null);
  const H = height, padL = 40, padR = 8, padT = 14, padB = 30;
  const n = data.length;
  const yMax = niceMax(Math.max(1, ...data.map(d => d.value)));
  const bw = (W - padL - padR) / n;
  const barW = Math.min(28, bw * 0.62);
  const yOf = (v) => padT + (H - padT - padB) * (1 - v / yMax);
  const ticks = 4;
  return (
    <div ref={ref} style={{ position: "relative", width: "100%" }} onMouseLeave={() => setHover(null)}>
      <svg width={W} height={H} style={{ display: "block" }}>
        {Array.from({ length: ticks + 1 }, (_, i) => yMax * i / ticks).map((g, i) => (
          <g key={i}>
            <line x1={padL} x2={W - padR} y1={yOf(g)} y2={yOf(g)} stroke="var(--grid)" strokeWidth="1" />
            <text x={padL - 7} y={yOf(g) + 3.5} textAnchor="end" fontSize="10" fill="var(--text-3)" fontFamily="Geist Mono, monospace">{formatY(g)}</text>
          </g>
        ))}
        {data.map((d, i) => {
          const x = padL + bw * i + (bw - barW) / 2;
          const h = Math.max(0, yOf(0) - yOf(d.value));
          return (
            <g key={i} onMouseEnter={e => setHover({ i, clientX: e.clientX, clientY: e.clientY })}
               onMouseMove={e => setHover({ i, clientX: e.clientX, clientY: e.clientY })}>
              <rect x={padL + bw * i} y={padT} width={bw} height={H - padT - padB} fill="transparent" />
              {d.value > 0
                ? <rect x={x} y={yOf(d.value)} width={barW} height={h} rx="4"
                        fill={color} opacity={hover && hover.i === i ? 1 : (d.faint ? 0.5 : 0.85)} />
                : <line x1={x} x2={x + barW} y1={yOf(0)} y2={yOf(0)} stroke="var(--border-strong)" strokeWidth="2" />}
              {(i % Math.ceil(n / Math.max(7, Math.floor(W / 70))) === 0) &&
                <text x={padL + bw * i + bw / 2} y={H - 14} textAnchor="middle" fontSize="10" fill="var(--text-3)" fontFamily="Geist Mono, monospace">{d.label}</text>}
              {d.sub && <text x={padL + bw * i + bw / 2} y={H - 3} textAnchor="middle" fontSize="9" fill="var(--text-3)">{d.sub}</text>}
            </g>
          );
        })}
      </svg>
      {hover &&
        <ChartTip clientX={hover.clientX} clientY={hover.clientY} title={data[hover.i].full || data[hover.i].label}
          rows={[{ name: "Projected", color, value: formatY(data[hover.i].value) + " fills" }]} />}
    </div>
  );
}

/* ---------- Sparkline ---------- */
function Spark({ values, color = "var(--accent)", w = 96, h = 30, filled = true }) {
  const max = Math.max(1, ...values), min = Math.min(...values);
  const n = values.length;
  const xOf = i => (w) * (i / (n - 1));
  const yOf = v => 2 + (h - 4) * (1 - (v - min) / (max - min || 1));
  const line = values.map((v, i) => (i ? "L" : "M") + xOf(i).toFixed(1) + " " + yOf(v).toFixed(1)).join(" ");
  return (
    <svg className="spark" width={w} height={h} style={{ display: "block" }}>
      {filled && <path d={line + ` L${w} ${h} L0 ${h} Z`} fill={color} opacity="0.12" />}
      <path d={line} fill="none" stroke={color} strokeWidth="1.6" strokeLinejoin="round" strokeLinecap="round" />
      <circle cx={xOf(n - 1)} cy={yOf(values[n - 1])} r="2.2" fill={color} />
    </svg>
  );
}

/* ---------- Tooltip (fixed) ---------- */
function ChartTip({ clientX, clientY, title, rows }) {
  const ref = useRef();
  const [pos, setPos] = useState({ left: clientX + 14, top: clientY - 10 });
  useEffect(() => {
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    let left = clientX + 14, top = clientY - r.height / 2;
    if (left + r.width > window.innerWidth - 8) left = clientX - r.width - 14;
    if (top < 8) top = 8;
    if (top + r.height > window.innerHeight - 8) top = window.innerHeight - r.height - 8;
    setPos({ left, top });
  }, [clientX, clientY]);
  return (
    <div ref={ref} className="chart-tip" style={{ left: pos.left, top: pos.top, opacity: 1 }}>
      <div className="tt">{title}</div>
      {rows.map((r, i) => (
        <div key={i} className="tr">
          <span className="nm"><i style={{ background: r.color }} />{r.name}</span>
          <span className="vv">{r.value}</span>
        </div>
      ))}
    </div>
  );
}

Object.assign(window, { AreaLine, VBars, Spark, ChartTip, useWidth, niceMax });
