// Charts & data viz: gauge, trend line, sparkline, sensor waveforms.

function useCountUp(target, duration = 1600) {
  const [v, setV] = React.useState(0);
  React.useEffect(() => {
    let raf, start;
    const tick = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setV(target * eased);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, duration]);
  return v;
}

function Gauge({ value = 86, label = 'Oral Health Score' }) {
  const v = useCountUp(value, 1800);
  const R = 92, C = 2 * Math.PI * R;
  const arcRange = 0.72; // 72% of the circle is the arc
  const dashTotal = C * arcRange;
  const fillLen = (v / 100) * dashTotal;
  return (
    <div className="gauge">
      <svg viewBox="0 0 220 220">
        <circle cx="110" cy="110" r={R} className="track" strokeWidth="14" fill="none"
          strokeDasharray={`${dashTotal} ${C - dashTotal}`} strokeLinecap="round"/>
        <circle cx="110" cy="110" r={R} className="fill" strokeWidth="14" fill="none"
          strokeDasharray={`${fillLen} ${C - fillLen}`} strokeLinecap="round"/>
      </svg>
      <div className="center">
        <div>
          <div className="num">{Math.round(v)}</div>
          <div className="lbl">{label}</div>
        </div>
      </div>
    </div>
  );
}

// Build smooth path through points
function smoothPath(pts) {
  if (pts.length < 2) return '';
  let d = `M ${pts[0].x} ${pts[0].y}`;
  for (let i = 0; i < pts.length - 1; i++) {
    const p0 = pts[i - 1] || pts[i];
    const p1 = pts[i];
    const p2 = pts[i + 1];
    const p3 = pts[i + 2] || p2;
    const cp1x = p1.x + (p2.x - p0.x) / 6;
    const cp1y = p1.y + (p2.y - p0.y) / 6;
    const cp2x = p2.x - (p3.x - p1.x) / 6;
    const cp2y = p2.y - (p3.y - p1.y) / 6;
    d += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`;
  }
  return d;
}

function TrendChart({ series, secondary, range = '90D' }) {
  // series: array of {label, value}
  const W = 560, H = 240, pad = { t: 16, r: 8, b: 28, l: 32 };
  const innerW = W - pad.l - pad.r;
  const innerH = H - pad.t - pad.b;
  const minY = 50, maxY = 100;
  const xStep = innerW / (series.length - 1);
  const toX = (i) => pad.l + i * xStep;
  const toY = (v) => pad.t + innerH - ((v - minY) / (maxY - minY)) * innerH;
  const pts  = series.map((d, i) => ({ x: toX(i), y: toY(d.value) }));
  const pts2 = secondary?.map((d, i) => ({ x: toX(i), y: toY(d.value) }));
  const path  = smoothPath(pts);
  const path2 = pts2 ? smoothPath(pts2) : null;
  const area  = `${path} L ${pts[pts.length-1].x} ${pad.t + innerH} L ${pts[0].x} ${pad.t + innerH} Z`;

  const ticks = [60, 70, 80, 90, 100];

  const [hover, setHover] = React.useState(null);
  const wrapRef = React.useRef(null);
  function onMove(e) {
    const rect = wrapRef.current.getBoundingClientRect();
    const x = (e.clientX - rect.left) * (W / rect.width);
    const i = Math.max(0, Math.min(series.length - 1, Math.round((x - pad.l) / xStep)));
    setHover(i);
  }

  return (
    <div className="chart-wrap" ref={wrapRef}
         onMouseMove={onMove} onMouseLeave={() => setHover(null)}>
      <svg viewBox={`0 0 ${W} ${H}`}>
        <defs>
          <linearGradient id="areaGrad" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="var(--accent)" stopOpacity=".22"/>
            <stop offset="100%" stopColor="var(--accent)" stopOpacity="0"/>
          </linearGradient>
        </defs>
        {/* gridlines */}
        {ticks.map(t => (
          <g key={t}>
            <line className="gridline" x1={pad.l} x2={W - pad.r} y1={toY(t)} y2={toY(t)} />
            <text className="axis-lbl" x={pad.l - 8} y={toY(t) + 3} textAnchor="end">{t}</text>
          </g>
        ))}
        {/* x labels (every 3rd) */}
        {series.map((d, i) =>
          (i % Math.ceil(series.length / 6) === 0 || i === series.length - 1) && (
            <text key={i} className="axis-lbl" x={toX(i)} y={H - 10} textAnchor="middle">{d.label}</text>
          )
        )}
        {/* secondary (cohort) */}
        {path2 && <path className="line2" d={path2} />}
        {/* primary */}
        <path className="area" d={area} />
        <path className="line" d={path} />
        {/* dots only at endpoints + hover */}
        <circle className="dot" cx={pts[pts.length-1].x} cy={pts[pts.length-1].y} style={{animationDelay:'1.8s'}}/>
        {hover != null && (
          <g style={{ pointerEvents: 'none' }}>
            <line className="hover-line" x1={pts[hover].x} x2={pts[hover].x} y1={pad.t} y2={pad.t + innerH}/>
            <circle cx={pts[hover].x} cy={pts[hover].y} r="5" fill="var(--paper-2)" stroke="var(--accent)" strokeWidth="2"/>
            {/* label */}
            <g transform={`translate(${pts[hover].x},${pts[hover].y - 18})`}>
              <rect x="-30" y="-18" width="60" height="22" rx="6" className="label-pill"/>
              <text textAnchor="middle" y="-2" fontSize="11" fontFamily="JetBrains Mono, monospace" fill="var(--ink)">
                {series[hover].value}
              </text>
            </g>
          </g>
        )}
      </svg>
    </div>
  );
}

function Sparkline({ data, w = 80, h = 24 }) {
  const min = Math.min(...data), max = Math.max(...data);
  const xs = w / (data.length - 1);
  const path = data.map((v, i) => {
    const x = i * xs;
    const y = h - ((v - min) / (max - min || 1)) * h;
    return (i === 0 ? 'M' : 'L') + ` ${x.toFixed(1)} ${y.toFixed(1)}`;
  }).join(' ');
  return (
    <svg className="spark" viewBox={`0 0 ${w} ${h}`}>
      <path d={path} />
    </svg>
  );
}

// audio bars (live, animated)
function AudioBars() {
  const [seed, setSeed] = React.useState(0);
  React.useEffect(() => {
    const t = setInterval(() => setSeed(s => s + 1), 110);
    return () => clearInterval(t);
  }, []);
  const N = 36;
  return (
    <svg className="wave" viewBox={`0 0 ${N * 6} 48`} preserveAspectRatio="none">
      {Array.from({ length: N }).map((_, i) => {
        const phase = (i / N) * Math.PI * 2 + seed * 0.4;
        const env = Math.sin((i + seed) * 0.3) * 0.5 + 0.5;
        const h = 6 + (Math.abs(Math.sin(phase)) * 0.7 + env * 0.3) * 36;
        const y = (48 - h) / 2;
        return <rect key={i} x={i * 6 + 1} y={y} width="3.4" height={h} rx="1.5"
                     fill="var(--accent)" opacity={0.5 + env * 0.5}/>;
      })}
    </svg>
  );
}

// breath sensor: scrolling line graph
function BreathLine() {
  const [pts, setPts] = React.useState(() =>
    Array.from({ length: 60 }, () => 38 + Math.random() * 14)
  );
  React.useEffect(() => {
    const t = setInterval(() => {
      setPts(prev => {
        const last = prev[prev.length - 1];
        const next = Math.max(28, Math.min(78,
          last + (Math.random() - 0.5) * 9 + Math.sin(Date.now() / 600) * 2));
        return [...prev.slice(1), next];
      });
    }, 200);
    return () => clearInterval(t);
  }, []);
  const W = 240, H = 48;
  const dx = W / (pts.length - 1);
  const path = pts.map((v, i) => `${i === 0 ? 'M' : 'L'} ${i * dx} ${H - (v / 100) * H}`).join(' ');
  const area = `${path} L ${W} ${H} L 0 ${H} Z`;
  return (
    <svg className="wave" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none">
      <defs>
        <linearGradient id="bgrad" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--accent)" stopOpacity=".22"/>
          <stop offset="100%" stopColor="var(--accent)" stopOpacity="0"/>
        </linearGradient>
      </defs>
      <path d={area} fill="url(#bgrad)"/>
      <path d={path} fill="none" stroke="var(--accent)" strokeWidth="1.5"/>
    </svg>
  );
}

window.Gauge = Gauge;
window.TrendChart = TrendChart;
window.Sparkline = Sparkline;
window.AudioBars = AudioBars;
window.BreathLine = BreathLine;
