/* Loopette — animated infinity-tape cassette
   Drag-to-spin interaction (visual only, no audio)
*/

const { useState, useEffect, useRef } = React;

function Cassette({ label = "MIX TAPE", time = "0:10.33" }) {
  const wrapRef = useRef(null);
  const [spin, setSpin] = useState(0);          // current rotation deg
  const [speed, setSpeed] = useState(60);       // deg/sec — natural cruise
  const [isDragging, setIsDragging] = useState(false);
  const [playhead, setPlayhead] = useState(0.42);

  // animation tick
  useEffect(() => {
    let raf, last = performance.now();
    const tick = (now) => {
      const dt = (now - last) / 1000;
      last = now;
      if (!isDragging) {
        setSpin((s) => s + speed * dt);
        setPlayhead((p) => (p + dt * 0.06) % 1);
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [isDragging, speed]);

  // drag handling — drag horizontally to scrub the tape
  useEffect(() => {
    const el = wrapRef.current;
    if (!el) return;
    let lastX = 0, lastT = 0, vx = 0;

    const onDown = (e) => {
      const x = e.touches ? e.touches[0].clientX : e.clientX;
      lastX = x; lastT = performance.now();
      setIsDragging(true);
    };
    const onMove = (e) => {
      if (!el.dataset.dragging) return;
    };
    const onMoveActive = (e) => {
      const x = e.touches ? e.touches[0].clientX : e.clientX;
      const t = performance.now();
      const dx = x - lastX;
      const dt = Math.max(1, t - lastT);
      vx = dx / dt;            // px per ms
      setSpin((s) => s + dx * 1.4);
      setPlayhead((p) => Math.max(0, Math.min(1, p + dx / 600)));
      lastX = x; lastT = t;
    };
    const onUp = () => {
      setIsDragging(false);
      // throw — convert velocity into spin speed, then decay back
      const newSpeed = 60 + vx * 200;
      setSpeed(newSpeed);
      // ease back to cruise speed
      setTimeout(() => setSpeed(60), 800);
    };

    let dragging = false;
    const handleDown = (e) => { dragging = true; el.dataset.dragging = "1"; onDown(e); };
    const handleMove = (e) => { if (dragging) onMoveActive(e); };
    const handleUp   = ()  => { if (dragging) { dragging = false; delete el.dataset.dragging; onUp(); } };

    el.addEventListener('mousedown', handleDown);
    window.addEventListener('mousemove', handleMove);
    window.addEventListener('mouseup', handleUp);
    el.addEventListener('touchstart', handleDown, { passive: true });
    window.addEventListener('touchmove', handleMove, { passive: true });
    window.addEventListener('touchend', handleUp);

    return () => {
      el.removeEventListener('mousedown', handleDown);
      window.removeEventListener('mousemove', handleMove);
      window.removeEventListener('mouseup', handleUp);
      el.removeEventListener('touchstart', handleDown);
      window.removeEventListener('touchmove', handleMove);
      window.removeEventListener('touchend', handleUp);
    };
  }, []);

  // generate 60-bar waveform (deterministic)
  const bars = Array.from({ length: 60 }, (_, i) => {
    const v = 0.5 + 0.5 * Math.sin(i * 0.7) * Math.cos(i * 0.31) * Math.sin(i * 0.11 + 1.2);
    return Math.max(0.18, Math.abs(v));
  });

  return (
    <div
      ref={wrapRef}
      className="lp-cassette"
      style={{ cursor: isDragging ? 'grabbing' : 'grab', userSelect: 'none', touchAction: 'pan-y' }}
    >
      <div className="lp-cassette-label">{label}</div>

      <div className="lp-cassette-window">
        <div className="lp-spool" style={{ transform: `rotate(${spin}deg)`, animation: 'none' }}>
          <div className="lp-spool-hub" />
        </div>

        {/* infinity ribbon — static path, dot tracks playhead */}
        <InfinityTape playhead={playhead} />

        <div className="lp-spool" style={{ transform: `rotate(${-spin * 0.95}deg)`, animation: 'none' }}>
          <div className="lp-spool-hub" />
        </div>
      </div>

      <div className="lp-waveform">
        {bars.map((h, i) => (
          <div
            key={i}
            className="lp-waveform-bar"
            style={{
              height: `${h * 100}%`,
              opacity: i / bars.length < playhead ? 0.95 : 0.35,
              background: i / bars.length < playhead ? 'var(--lp-teal)' : 'var(--lp-yellow)',
            }}
          />
        ))}
        <div
          className="lp-waveform-playhead"
          style={{ left: `calc(6% + ${playhead * 88}%)` }}
        />
      </div>

      <div className="lp-cassette-time">{time}</div>
    </div>
  );
}

function InfinityTape({ playhead }) {
  // static lemniscate path; the teal dot rides the curve at `playhead` (0..1)
  const pathRef = useRef(null);
  const [dot, setDot] = useState({ x: 100, y: 50 });

  useEffect(() => {
    const p = pathRef.current;
    if (!p) return;
    const len = p.getTotalLength();
    const pt = p.getPointAtLength((playhead % 1) * len);
    setDot({ x: pt.x, y: pt.y });
  }, [playhead]);

  // figure-8 / lemniscate, single continuous stroke
  const d = "M 50 50 C 50 20, 90 20, 100 50 C 110 80, 150 80, 150 50 C 150 20, 110 20, 100 50 C 90 80, 50 80, 50 50 Z";

  return (
    <div style={{
      position: 'relative',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
    }}>
      <svg viewBox="0 0 200 100" className="lp-tape-ribbon" style={{ maxWidth: '100%' }}>
        <defs>
          <linearGradient id="tape-grad" x1="0" x2="1">
            <stop offset="0" stopColor="#E8C341" />
            <stop offset="0.5" stopColor="#E8693A" />
            <stop offset="1" stopColor="#E8C341" />
          </linearGradient>
        </defs>
        <path
          ref={pathRef}
          d={d}
          fill="none"
          stroke="url(#tape-grad)"
          strokeWidth="9"
          strokeLinecap="round"
        />
        <path
          d={d}
          fill="none"
          stroke="rgba(0,0,0,0.35)"
          strokeWidth="3"
          strokeLinecap="round"
          strokeDasharray="2 4"
        />
        {/* travelling pivot dot — aligned with playhead */}
        <circle cx={dot.x} cy={dot.y} r="4.5" fill="#4FC8B8"
          style={{ filter: 'drop-shadow(0 0 4px #4FC8B8)' }} />
      </svg>
    </div>
  );
}

window.Cassette = Cassette;
