// TapTheBeat — rhythm minigame for filling dead time in lobby/locked screens.
// Ported from design2/hitster/project/e2e-screens.jsx.
// A bar of dots scrolls right→left through a hit-zone. Tap (or press Space)
// when a dot is over the zone. Perfect = 100 + combo, Good = 50, Miss = 0.

(() => {
const R = window.R;

function TapTheBeat() {
  const BPM = 96;
  const BEAT_MS = 60000 / BPM;
  const TRACK_W = 280;
  const HIT_X = 38;
  const HIT_TOL_PERFECT = 14;
  const HIT_TOL_GOOD = 28;
  const TRAVEL_MS = 2400;

  const [score, setScore] = React.useState(0);
  const [combo, setCombo] = React.useState(0);
  const [feedback, setFeedback] = React.useState(null);
  const startRef = React.useRef(performance.now());
  const beatsRef = React.useRef([]);
  const judgedRef = React.useRef(new Set());
  const [, forceTick] = React.useReducer((x) => x + 1, 0);

  // Spawn beats on a steady tempo
  React.useEffect(() => {
    let id = 0;
    const spawn = () => {
      id += 1;
      beatsRef.current.push({ id, spawnT: performance.now() });
      beatsRef.current = beatsRef.current.filter((b) => performance.now() - b.spawnT < TRAVEL_MS + 600);
    };
    spawn();
    const iv = setInterval(spawn, BEAT_MS * 2);
    return () => clearInterval(iv);
  }, []);

  // Animation tick + auto-judge missed dots
  React.useEffect(() => {
    let raf;
    const loop = () => {
      forceTick();
      const now = performance.now();
      beatsRef.current.forEach((b) => {
        if (judgedRef.current.has(b.id)) return;
        const age = now - b.spawnT;
        const x = TRACK_W - (age / TRAVEL_MS) * TRACK_W;
        if (x < HIT_X - HIT_TOL_GOOD - 6) {
          judgedRef.current.add(b.id);
          setCombo(0);
          setFeedback({ kind: 'miss', id: b.id, t: now });
        }
      });
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, []);

  const tap = React.useCallback(() => {
    const now = performance.now();
    let bestB = null, bestDist = Infinity;
    beatsRef.current.forEach((b) => {
      if (judgedRef.current.has(b.id)) return;
      const age = now - b.spawnT;
      const x = TRACK_W - (age / TRAVEL_MS) * TRACK_W;
      const dist = Math.abs(x - HIT_X);
      if (dist < bestDist) { bestDist = dist; bestB = b; }
    });
    if (!bestB) return;
    if (bestDist <= HIT_TOL_PERFECT) {
      judgedRef.current.add(bestB.id);
      setScore((s) => s + 100 + combo * 5);
      setCombo((c) => c + 1);
      setFeedback({ kind: 'perfect', id: bestB.id, t: now });
      try { navigator.vibrate?.(8); } catch (_) {}
    } else if (bestDist <= HIT_TOL_GOOD) {
      judgedRef.current.add(bestB.id);
      setScore((s) => s + 50);
      setCombo((c) => c + 1);
      setFeedback({ kind: 'good', id: bestB.id, t: now });
    } else {
      setCombo(0);
      setFeedback({ kind: 'early', id: 'x' + now, t: now });
    }
  }, [combo]);

  React.useEffect(() => {
    const k = (e) => { if (e.code === 'Space' || e.code === 'Enter') { e.preventDefault(); tap(); } };
    window.addEventListener('keydown', k);
    return () => window.removeEventListener('keydown', k);
  }, [tap]);

  const fbColor = feedback?.kind === 'perfect' ? R.yellow
                : feedback?.kind === 'good'    ? R.green
                : feedback?.kind === 'miss'    ? R.pink
                : R.muted;
  const fbLabel = feedback?.kind === 'perfect' ? 'PERFECT'
                : feedback?.kind === 'good'    ? 'GOOD'
                : feedback?.kind === 'miss'    ? 'MISS'
                : 'EARLY';

  const now = performance.now();
  const visible = beatsRef.current.map((b) => {
    const age = now - b.spawnT;
    const x = TRACK_W - (age / TRAVEL_MS) * TRACK_W;
    if (x < -10 || x > TRACK_W + 10) return null;
    return { ...b, x, judged: judgedRef.current.has(b.id) };
  }).filter(Boolean);
  const pulseScale = 1 + ((Date.now() % BEAT_MS) / BEAT_MS) * 0.18;

  return (
    <div style={{
      padding: '12px 14px', background: R.ink, color: R.paper,
      border: `2px solid ${R.ink}`, boxShadow: `4px 4px 0 ${R.ink}`,
      backgroundImage: window.risoTex('rgba(245,239,226,.06)', 3),
      position: 'relative',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
        <div style={{ fontSize: 10, fontWeight: 900, letterSpacing: '.24em', color: R.yellow }}>WHILE YOU WAIT</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          {combo >= 3 && (
            <div style={{ fontSize: 10, fontWeight: 900, letterSpacing: '.18em', color: R.pink }}>×{combo} COMBO</div>
          )}
          <div style={{
            fontSize: 14, fontWeight: 900, fontFamily: 'ui-monospace, monospace',
            color: R.paper, padding: '2px 8px', background: 'rgba(255,255,255,.08)',
          }}>{score}</div>
        </div>
      </div>

      <div style={{ fontSize: 14, fontWeight: 900, letterSpacing: '-.01em', marginBottom: 8 }}>
        Tap the beat <span style={{ fontSize: 9, fontWeight: 700, color: 'rgba(245,239,226,.5)', letterSpacing: '.14em' }}>· TAP OR SPACE</span>
      </div>

      <div
        onPointerDown={(e) => { e.preventDefault(); tap(); }}
        style={{
          position: 'relative', width: '100%', height: 56,
          background: 'rgba(245,239,226,.06)',
          border: `1px solid rgba(245,239,226,.2)`,
          overflow: 'hidden', cursor: 'pointer',
          touchAction: 'manipulation', userSelect: 'none',
        }}>
        {/* Hit zone */}
        <div style={{
          position: 'absolute', left: HIT_X - 18, top: 8, width: 36, height: 40,
          border: `2px solid ${R.yellow}`, background: `rgba(255,210,63,.1)`,
          transform: `scale(${pulseScale})`, transformOrigin: 'center',
          transition: 'transform .12s',
        }} />
        {/* Beat dots */}
        {visible.map((b) => (
          <div key={b.id} style={{
            position: 'absolute',
            left: b.x - 12, top: 16,
            width: 24, height: 24,
            background: b.judged ? 'transparent' : R.pink,
            border: `2px solid ${b.judged ? 'rgba(255,90,122,.3)' : R.paper}`,
            borderRadius: '50%',
            transition: 'background .1s, border-color .1s',
            boxShadow: b.judged ? 'none' : `0 0 0 4px rgba(255,90,122,.2)`,
          }} />
        ))}
        {feedback && (
          <div key={feedback.id} style={{
            position: 'absolute', left: HIT_X - 30, top: 4, width: 60,
            textAlign: 'center', color: fbColor,
            fontSize: 11, fontWeight: 900, letterSpacing: '.16em',
            animation: 'fbpop .5s forwards',
            pointerEvents: 'none',
          }}>{fbLabel}</div>
        )}
      </div>
      <style>{`@keyframes fbpop {
        0% { opacity: 0; transform: translateY(0) scale(.8); }
        20% { opacity: 1; transform: translateY(-6px) scale(1.1); }
        100% { opacity: 0; transform: translateY(-22px) scale(1); }
      }`}</style>
    </div>
  );
}

window.TapTheBeat = TapTheBeat;
})();
