/* global React, Icon, IconBtn, db */
const { useState, useEffect, useRef, useCallback } = React;

// ============================================================
// Refresh-rate tester — measures how many times per second the
// display actually repaints, using requestAnimationFrame deltas.
// Shows the detected Hz, a live frame-time graph, and a sweeping
// bar for spotting stutter or tearing by eye.
// ============================================================

// Snap a noisy measurement to the closest common panel rate.
const RRT_COMMON = [24, 30, 48, 50, 60, 72, 75, 90, 100, 120, 144, 165, 240, 360];
function rrtSnap(hz) {
  if (!hz) return 0;
  let best = RRT_COMMON[0], bd = Infinity;
  for (const c of RRT_COMMON) { const d = Math.abs(c - hz); if (d < bd) { bd = d; best = c; } }
  // only snap if we're within 6% — otherwise show the raw rounded value
  return bd / best <= 0.06 ? best : Math.round(hz);
}

function RefreshRateTool() {
  const [running, setRunning] = useState(false);
  const [hz, setHz] = useState(0);          // smoothed instantaneous
  const [detected, setDetected] = useState(0);
  const [stats, setStats] = useState({ min: 0, max: 0, avg: 0, frames: 0 });
  const [speed, setSpeed] = useState(1);     // sweep speed multiplier

  const rafRef = useRef(0);
  const graphRef = useRef(null);
  const sweepRef = useRef(null);
  const state = useRef({ last: 0, deltas: [], emaHz: 0, allFt: [], start: 0, sweepX: 0, lastT: 0 });

  const stop = useCallback(() => {
    cancelAnimationFrame(rafRef.current);
    setRunning(false);
  }, []);

  useEffect(() => () => cancelAnimationFrame(rafRef.current), []);

  const drawGraph = useCallback(() => {
    const canvas = graphRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const dpr = window.devicePixelRatio || 1;
    const w = canvas.clientWidth, h = canvas.clientHeight;
    if (canvas.width !== w * dpr || canvas.height !== h * dpr) { canvas.width = w * dpr; canvas.height = h * dpr; }
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    ctx.clearRect(0, 0, w, h);

    const style = getComputedStyle(document.documentElement);
    const border = style.getPropertyValue('--color-border').trim() || '#2d1b40';
    const primary = style.getPropertyValue('--color-primary').trim() || '#7f0df2';
    const muted = style.getPropertyValue('--text-faint').trim() || '#475569';

    const deltas = state.current.deltas;
    if (!deltas.length) return;

    // Frame-time scale: 0..~40ms window so 60fps (16.7ms) sits mid-low.
    const maxFt = 40;
    // reference line at the current target (median)
    const sorted = [...deltas].sort((a, b) => a - b);
    const median = sorted[Math.floor(sorted.length / 2)];
    const refY = h - (Math.min(median, maxFt) / maxFt) * h;
    ctx.strokeStyle = muted; ctx.lineWidth = 1; ctx.setLineDash([4, 4]);
    ctx.beginPath(); ctx.moveTo(0, refY); ctx.lineTo(w, refY); ctx.stroke();
    ctx.setLineDash([]);

    ctx.strokeStyle = primary; ctx.lineWidth = 1.5; ctx.lineJoin = 'round';
    ctx.beginPath();
    const n = deltas.length;
    for (let i = 0; i < n; i++) {
      const x = (i / (n - 1 || 1)) * w;
      const y = h - (Math.min(deltas[i], maxFt) / maxFt) * h;
      i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
    }
    ctx.stroke();
  }, []);

  const loop = useCallback((t) => {
    const s = state.current;
    if (s.last) {
      const dt = t - s.last;
      if (dt > 0 && dt < 1000) {
        s.deltas.push(dt);
        if (s.deltas.length > 240) s.deltas.shift();
        s.allFt.push(dt);
        const inst = 1000 / dt;
        s.emaHz = s.emaHz ? s.emaHz * 0.9 + inst * 0.1 : inst;
      }
      // advance the sweep bar (px/sec scaled by speed), independent of Hz
      const track = sweepRef.current;
      if (track) {
        const tw = track.parentElement.clientWidth;
        s.sweepX += (tw * 0.6 * speed) * (dt / 1000);
        if (s.sweepX > tw) s.sweepX = -tw * 0.22;
        track.style.transform = `translateX(${s.sweepX}px)`;
      }
    }
    s.last = t;

    // throttle React state updates to ~6/sec
    if (!s.lastT || t - s.lastT > 160) {
      s.lastT = t;
      setHz(s.emaHz);
      setDetected(rrtSnap(s.emaHz));
      if (s.allFt.length) {
        const arr = s.allFt;
        let mn = Infinity, mx = 0, sum = 0;
        for (const d of arr) { const f = 1000 / d; if (f < mn) mn = f; if (f > mx) mx = f; sum += f; }
        setStats({ min: Math.round(mn), max: Math.round(mx), avg: Math.round(sum / arr.length), frames: arr.length });
      }
      drawGraph();
    }
    rafRef.current = requestAnimationFrame(loop);
  }, [drawGraph, speed]);

  const start = useCallback(() => {
    state.current = { last: 0, deltas: [], emaHz: 0, allFt: [], start: performance.now(), sweepX: 0, lastT: 0 };
    setRunning(true);
    cancelAnimationFrame(rafRef.current);
    rafRef.current = requestAnimationFrame(loop);
  }, [loop]);

  // keep loop fresh when speed changes mid-run
  useEffect(() => {
    if (running) { cancelAnimationFrame(rafRef.current); rafRef.current = requestAnimationFrame(loop); }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [loop, running]);

  const reset = () => { stop(); setHz(0); setDetected(0); setStats({ min: 0, max: 0, avg: 0, frames: 0 }); if (graphRef.current) { const c = graphRef.current.getContext('2d'); c && c.clearRect(0, 0, graphRef.current.width, graphRef.current.height); } };

  return (
    <div className="hw-tool">
      <div className="hw-head">
        <div>
          <h1>Refresh-rate tester</h1>
          <p>Measures how many frames per second your display actually paints — the real refresh
             rate, not just the spec. Run it for a few seconds; the graph and sweep reveal stutter.</p>
        </div>
        <div className="hw-head-actions">
          <span className={`hw-live ${running ? 'is-on' : ''}`}>
            <span className="hw-live-dot" />{running ? 'Sampling' : 'Idle'}
          </span>
          {running
            ? <button className="lh-btn ghost" onClick={stop}><Icon name="stop" />Stop</button>
            : <button className="lh-btn primary" onClick={start}><Icon name="speed" />Start test</button>}
          <button className="lh-btn ghost" onClick={reset}><Icon name="restart_alt" />Reset</button>
        </div>
      </div>

      <div className="hw-panel">
        <div className="rrt-hero">
          <span className={`rrt-hz ${detected ? '' : 'is-idle'}`}>{detected || '––'}</span>
          <span className="rrt-unit">Hz</span>
          {running && hz ? (
            <span style={{ marginLeft: 'auto', fontSize: 13, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>
              live {hz.toFixed(1)} Hz · {(1000 / hz).toFixed(1)} ms/frame
            </span>
          ) : null}
        </div>

        <div className="rrt-graph-wrap">
          <canvas ref={graphRef} className="rrt-graph" />
        </div>
        <div className="rrt-note">Frame time over the last ~4 seconds. A flat line means steady pacing; spikes are dropped frames.</div>

        <div className="hw-stats" style={{ marginTop: 16 }}>
          <div className="hw-stat">
            <div className="hw-stat-value">{stats.avg || '—'}</div>
            <div className="hw-stat-label">Average fps</div>
          </div>
          <div className="hw-stat">
            <div className="hw-stat-value">{stats.min || '—'}</div>
            <div className="hw-stat-label">Min fps</div>
          </div>
          <div className="hw-stat">
            <div className="hw-stat-value">{stats.max || '—'}</div>
            <div className="hw-stat-label">Max fps</div>
          </div>
          <div className="hw-stat">
            <div className="hw-stat-value">{stats.frames || '—'}</div>
            <div className="hw-stat-label">Frames sampled</div>
          </div>
        </div>
      </div>

      <div className="hw-panel">
        <div className="hw-toolbar" style={{ marginBottom: 14 }}>
          <div style={{ fontSize: 14, fontWeight: 650, color: 'var(--text-hi)' }}>Motion smoothness check</div>
          <div className="hw-spacer" />
          <div className="hw-seg">
            {[0.5, 1, 2].map(sp => (
              <button key={sp} className={speed === sp ? 'is-active' : ''} onClick={() => setSpeed(sp)}>{sp}×</button>
            ))}
          </div>
        </div>
        <div className="rrt-sweep-track">
          <div ref={sweepRef} className="rrt-sweep-bar" />
        </div>
        <div className="rrt-note">Watch the bar glide. On a healthy display it moves perfectly smoothly; juddering or a torn edge points to dropped frames or v-sync issues.</div>
      </div>
    </div>
  );
}

window.RefreshRateTool = RefreshRateTool;
