// === Interactive playground — 9 modes ===
const PLAY_MODES = [
  { id: 'orbit', label: '◉ Orbit', hint: 'particles fall toward / away from cursor' },
  { id: 'gravity', label: '↡ Gravity', hint: 'rain that bounces off the floor' },
  { id: 'flock', label: '⟿ Flock', hint: 'a school chasing the cursor' },
  { id: 'spring', label: '~ Spring', hint: 'each particle springs back to its home' },
  { id: 'vortex', label: '⌀ Vortex', hint: 'a slow tornado around the cursor' },
  { id: 'magnet', label: '⌖ Magnet', hint: 'click+hold to repel, release to attract' },
  { id: 'trails', label: '✶ Trails', hint: 'long fading streaks' },
  { id: 'swarm', label: '※ Swarm', hint: 'noise-driven curl flow field' },
  { id: 'chain', label: '∞ Chain', hint: 'connected like a string of pearls' },
];

const COLOR_PALETTES = {
  warm: () => 30 + Math.random() * 60, // amber/orange
  cool: () => 200 + Math.random() * 60, // teal/blue
  rainbow: () => Math.random() * 360,
  mono: () => 0,
};

function Playground() {
  const canvasRef = useRef(null);
  const [t, setLocalT] = useState(window.__TWEAKS);

  useEffect(() => {
    const off = window.__onTweakChange((v) => setLocalT({ ...v }));
    return off;
  }, []);

  const stateRef = useRef({
    balls: [], home: [], pointer: { x: -9999, y: -9999, down: false },
    tw: window.__TWEAKS,
    time: 0,
  });

  useEffect(() => { stateRef.current.tw = t; }, [t]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const dpr = Math.min(window.devicePixelRatio || 1, 2);

    function size() {
      const r = canvas.getBoundingClientRect();
      canvas.width = r.width * dpr;
      canvas.height = r.height * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }
    size();
    const ro = new ResizeObserver(size);
    ro.observe(canvas);

    function reset() {
      const r = canvas.getBoundingClientRect();
      const tw = stateRef.current.tw;
      const palette = COLOR_PALETTES[tw.playColor] || COLOR_PALETTES.warm;
      stateRef.current.balls = [];
      stateRef.current.home = [];
      const n = tw.playCount;
      for (let i = 0; i < n; i++) {
        const x = Math.random() * r.width;
        const y = Math.random() * r.height;
        stateRef.current.balls.push({
          x, y,
          px: x, py: y, // prev position (for trails / chain)
          vx: (Math.random() - 0.5) * 2,
          vy: (Math.random() - 0.5) * 2,
          r: 4 + Math.random() * 6,
          h: palette(),
          phase: Math.random() * Math.PI * 2,
        });
        stateRef.current.home.push({ x, y });
      }
    }
    reset();

    let lastCount = stateRef.current.tw.playCount;
    let lastColor = stateRef.current.tw.playColor;
    const watch = setInterval(() => {
      const tw = stateRef.current.tw;
      if (tw.playCount !== lastCount || tw.playColor !== lastColor) {
        lastCount = tw.playCount; lastColor = tw.playColor;
        reset();
      }
    }, 200);

    function onMove(e) {
      const r = canvas.getBoundingClientRect();
      stateRef.current.pointer.x = e.clientX - r.left;
      stateRef.current.pointer.y = e.clientY - r.top;
    }
    function onLeave() { stateRef.current.pointer.x = stateRef.current.pointer.y = -9999; }
    function onDown() { stateRef.current.pointer.down = true; }
    function onUp() { stateRef.current.pointer.down = false; }
    canvas.addEventListener('mousemove', onMove);
    canvas.addEventListener('mouseleave', onLeave);
    canvas.addEventListener('mousedown', onDown);
    window.addEventListener('mouseup', onUp);
    canvas.addEventListener('touchmove', (e) => { const tt = e.touches[0]; onMove({ clientX: tt.clientX, clientY: tt.clientY }); }, { passive: true });

    let raf = 0;
    function tick() {
      const tw = stateRef.current.tw;
      const r = canvas.getBoundingClientRect();
      const W = r.width, H = r.height;
      stateRef.current.time += 0.016;

      const isLight = document.documentElement.getAttribute('data-theme') === 'light';
      const trails = tw.playTrails;

      if (trails) {
        ctx.fillStyle = isLight ? 'rgba(244,240,232,0.08)' : 'rgba(10,9,8,0.08)';
        ctx.fillRect(0, 0, W, H);
      } else {
        ctx.clearRect(0, 0, W, H);
        // grid
        ctx.strokeStyle = isLight ? 'rgba(26,24,21,0.05)' : 'rgba(243,239,230,0.05)';
        ctx.lineWidth = 1;
        const grid = 32;
        for (let x = 0; x < W; x += grid) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke(); }
        for (let y = 0; y < H; y += grid) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke(); }
      }

      const m = tw.playMode;
      const px = stateRef.current.pointer.x, py = stateRef.current.pointer.y;
      const downPull = stateRef.current.pointer.down ? -1 : 1;
      const friction = tw.playFriction;
      const grav = tw.playGravity;

      const balls = stateRef.current.balls;
      const home = stateRef.current.home;

      // mode-specific update
      for (let idx = 0; idx < balls.length; idx++) {
        const b = balls[idx];
        b.px = b.x; b.py = b.y;

        if (m === 'orbit') {
          if (px > -1000) {
            const dx = px - b.x, dy = py - b.y;
            const d2 = Math.max(40, dx*dx + dy*dy);
            const f = (300 / d2) * downPull;
            b.vx += dx * f * 0.02;
            b.vy += dy * f * 0.02;
          }
          b.vx *= friction; b.vy *= friction;
        } else if (m === 'gravity') {
          b.vy += 0.18 * grav;
          if (px > -1000) {
            const dx = px - b.x, dy = py - b.y;
            const d2 = dx*dx + dy*dy;
            if (d2 < 14400) {
              const d = Math.sqrt(d2) || 1;
              b.vx += (dx / d) * -0.4;
              b.vy += (dy / d) * -0.4;
            }
          }
          b.vx *= friction;
        } else if (m === 'flock') {
          if (px > -1000) {
            const dx = px - b.x, dy = py - b.y;
            const d = Math.sqrt(dx*dx + dy*dy) || 1;
            b.vx += (dx / d) * 0.12;
            b.vy += (dy / d) * 0.12;
          }
          b.vx += (Math.random() - 0.5) * 0.3;
          b.vy += (Math.random() - 0.5) * 0.3;
          const sp = Math.hypot(b.vx, b.vy);
          if (sp > 4) { b.vx = (b.vx / sp) * 4; b.vy = (b.vy / sp) * 4; }
        } else if (m === 'spring') {
          const h = home[idx];
          b.vx += (h.x - b.x) * 0.04;
          b.vy += (h.y - b.y) * 0.04;
          if (px > -1000) {
            const dx = b.x - px, dy = b.y - py;
            const d2 = dx*dx + dy*dy;
            if (d2 < 22500) {
              const d = Math.sqrt(d2) || 1;
              const f = (1 - d2 / 22500) * 6;
              b.vx += (dx / d) * f;
              b.vy += (dy / d) * f;
            }
          }
          b.vx *= 0.88; b.vy *= 0.88;
        } else if (m === 'vortex') {
          if (px > -1000) {
            const dx = b.x - px, dy = b.y - py;
            const d2 = dx*dx + dy*dy;
            const d = Math.sqrt(d2) || 1;
            // tangential velocity (perpendicular to radial)
            b.vx += (-dy / d) * 0.35 * downPull;
            b.vy += (dx / d) * 0.35 * downPull;
            // slight inward pull
            b.vx += (-dx / d) * 0.04;
            b.vy += (-dy / d) * 0.04;
          }
          b.vx *= 0.97; b.vy *= 0.97;
        } else if (m === 'magnet') {
          if (px > -1000) {
            const dx = px - b.x, dy = py - b.y;
            const d = Math.sqrt(dx*dx + dy*dy) || 1;
            const sign = stateRef.current.pointer.down ? -1 : 1;
            b.vx += (dx / d) * sign * 0.5;
            b.vy += (dy / d) * sign * 0.5;
          }
          b.vx *= 0.94; b.vy *= 0.94;
        } else if (m === 'trails') {
          if (px > -1000) {
            const dx = px - b.x, dy = py - b.y;
            const d = Math.sqrt(dx*dx + dy*dy) || 1;
            b.vx += (dx / d) * 0.18;
            b.vy += (dy / d) * 0.18;
          }
          b.vx += Math.cos(stateRef.current.time + b.phase) * 0.2;
          b.vy += Math.sin(stateRef.current.time + b.phase) * 0.2;
          b.vx *= 0.95; b.vy *= 0.95;
        } else if (m === 'swarm') {
          // curl noise approximation
          const t = stateRef.current.time;
          const ax = Math.sin(b.x * 0.01 + t) + Math.cos(b.y * 0.013 - t * 0.7);
          const ay = Math.cos(b.x * 0.014 - t * 0.9) + Math.sin(b.y * 0.011 + t);
          b.vx += ax * 0.15;
          b.vy += ay * 0.15;
          if (px > -1000) {
            const dx = px - b.x, dy = py - b.y;
            const d = Math.sqrt(dx*dx + dy*dy) || 1;
            b.vx += (dx / d) * 0.2;
            b.vy += (dy / d) * 0.2;
          }
          const sp = Math.hypot(b.vx, b.vy);
          if (sp > 3) { b.vx = (b.vx / sp) * 3; b.vy = (b.vy / sp) * 3; }
        } else if (m === 'chain') {
          if (idx === 0 && px > -1000) {
            b.vx += (px - b.x) * 0.2;
            b.vy += (py - b.y) * 0.2;
            b.vx *= 0.6; b.vy *= 0.6;
          } else if (idx > 0) {
            const prev = balls[idx - 1];
            const dx = prev.x - b.x, dy = prev.y - b.y;
            const d = Math.sqrt(dx*dx + dy*dy) || 1;
            const target = 14;
            const diff = (d - target) / d;
            b.vx += dx * diff * 0.5;
            b.vy += dy * diff * 0.5;
            b.vy += 0.05 * grav;
            b.vx *= 0.86; b.vy *= 0.86;
          }
        }

        b.x += b.vx; b.y += b.vy;
        if (b.x < b.r) { b.x = b.r; b.vx *= -0.7; }
        if (b.x > W - b.r) { b.x = W - b.r; b.vx *= -0.7; }
        if (b.y < b.r) { b.y = b.r; b.vy *= -0.7; }
        if (b.y > H - b.r) { b.y = H - b.r; b.vy *= -0.7; }
      }

      // draw
      if (m === 'chain') {
        ctx.strokeStyle = `oklch(0.78 0.14 ${balls[0]?.h || 60})`;
        ctx.lineWidth = 1.5;
        ctx.beginPath();
        for (let i = 0; i < balls.length; i++) {
          if (i === 0) ctx.moveTo(balls[i].x, balls[i].y);
          else ctx.lineTo(balls[i].x, balls[i].y);
        }
        ctx.stroke();
      }

      if (m === 'trails' && !trails) {
        // draw streaks even without persistence
        for (const b of balls) {
          ctx.strokeStyle = `oklch(0.78 0.14 ${b.h} / 0.5)`;
          ctx.lineWidth = b.r;
          ctx.lineCap = 'round';
          ctx.beginPath();
          ctx.moveTo(b.px, b.py);
          ctx.lineTo(b.x, b.y);
          ctx.stroke();
        }
      }

      for (const b of balls) {
        ctx.shadowColor = `oklch(0.78 0.14 ${b.h})`;
        ctx.shadowBlur = trails ? 8 : 14;
        ctx.fillStyle = `oklch(0.78 0.14 ${b.h})`;
        ctx.beginPath();
        ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
        ctx.fill();
        ctx.shadowBlur = 0;
      }

      raf = requestAnimationFrame(tick);
    }
    tick();

    return () => {
      cancelAnimationFrame(raf);
      ro.disconnect();
      clearInterval(watch);
      canvas.removeEventListener('mousemove', onMove);
      canvas.removeEventListener('mouseleave', onLeave);
      canvas.removeEventListener('mousedown', onDown);
      window.removeEventListener('mouseup', onUp);
    };
  }, []);

  const setMode = (id) => {
    window.__TWEAKS = { ...window.__TWEAKS, playMode: id };
    window.parent.postMessage({ type: '__edit_mode_set_keys', edits: { playMode: id } }, '*');
    window.__TWEAK_LISTENERS.forEach(cb => cb(window.__TWEAKS));
  };
  const setCount = (v) => {
    window.__TWEAKS = { ...window.__TWEAKS, playCount: v };
    window.parent.postMessage({ type: '__edit_mode_set_keys', edits: { playCount: v } }, '*');
    window.__TWEAK_LISTENERS.forEach(cb => cb(window.__TWEAKS));
  };

  const currentMode = PLAY_MODES.find(p => p.id === t.playMode) || PLAY_MODES[0];

  return (
    <section id="playground" data-screen-label="Playground">
      <div className="wrap">
        <div className="section-header">
          <h2 data-split-words>Playground — touch the screen.</h2>
          <span className="meta-num">05 / 06</span>
        </div>
        <p className="section-lede fade-up">
          Move your cursor inside the box. Pick a mode. Click & hold for a different effect. More controls live in the Tweaks panel.
        </p>

        <div className="play-card fade-up">
          <div className="play-controls">
            <div className="play-modes">
              {PLAY_MODES.map(opt => (
                <button
                  key={opt.id}
                  className={"play-mode " + (t.playMode === opt.id ? 'on' : '')}
                  onClick={() => setMode(opt.id)}
                  title={opt.hint}
                >{opt.label}</button>
              ))}
            </div>
            <div className="play-count">
              <span className="mono">particles</span>
              <input type="range" min="5" max="300" value={t.playCount} onChange={(e) => setCount(+e.target.value)} />
              <span className="mono play-count-num">{t.playCount}</span>
            </div>
          </div>
          <canvas ref={canvasRef} className="play-canvas" />
          <div className="play-hint mono">
            <span>↑ {currentMode.hint}</span>
            <span>· open Tweaks for color, gravity, friction, trails ·</span>
          </div>
        </div>
      </div>
    </section>
  );
}

window.Playground = Playground;
