/* ── Plinko ───────────────────────────────────────────────────────────────── */
function Plinko({ balance, apiFetch, onBack, onBalanceChange }) {
  const { useState, useEffect, useRef } = React;

  // ── Layout ────────────────────────────────────────────────────────────────
  const ROWS   = 12;
  const SLOTS  = ROWS + 1;          // 13
  const PS     = 30;                // peg spacing (px)
  const RH     = 32;                // row height (px)
  const TOP    = 30;                // y of first peg row
  const CX     = 210;               // board center X
  const SW     = 420;               // SVG / canvas width
  const SH     = 36;                // slot height
  const SY     = TOP + ROWS * RH + 12;   // slot Y = 426
  const SVG_H  = SY + SH + 14;          // = 476
  const BALL_R = 8;
  const PEG_R  = 4;

  // ── Physics ───────────────────────────────────────────────────────────────
  // With these constants a ball crosses the board in ~1.5 s and naturally
  // drifts PS/2 px per row, landing it in the correct slot.
  const GRAVITY   = 1000;   // px / s²
  const VY_DAMP   = 0.62;   // vy multiplier on each peg hit
  // IMPULSE chosen so that IMPULSE * row_time ≈ PS/2
  // row_time ≈ 0.126 s  →  IMPULSE ≈ 119
  const IMPULSE   = 120;    // px/s horizontal set at each peg

  // ── Visual data ───────────────────────────────────────────────────────────
  const MULTS  = [50, 12, 5, 2, 1, 0.5, 0.3, 0.5, 1, 2, 5, 12, 50];
  const COLORS = [
    '#d4a960','#c09038','#9a7030','#7a5c32','#6a5030',
    '#504028','#343022',
    '#504028','#6a5030','#7a5c32','#9a7030','#c09038','#d4a960',
  ];

  // ── State ─────────────────────────────────────────────────────────────────
  const [results,  setResults]  = useState([]);
  const [bet,      setBet]      = useState(100);
  const [err,      setErr]      = useState('');
  const [inFlight, setInFlight] = useState(0);

  const canvasRef  = useRef(null);
  const ballsRef   = useRef([]);   // mutable physics state, bypasses React
  const pingsRef   = useRef([]);   // { x, y, t } canvas-only ripples
  const nextId     = useRef(0);

  // Static peg positions (used for SVG render only)
  const pegs = [];
  for (let r = 0; r < ROWS; r++) {
    for (let j = 0; j <= r + 1; j++) {
      pegs.push({ x: CX + (2 * j - r - 1) * PS / 2, y: TOP + r * RH });
    }
  }

  const slotCX = k => CX + (k - ROWS / 2) * PS;

  // ── rAF physics + render loop ─────────────────────────────────────────────
  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    let rafId;
    let lastTs = null;

    function loop(ts) {
      if (!lastTs) lastTs = ts;
      const dt = Math.min((ts - lastTs) / 1000, 0.05); // cap at 50 ms
      lastTs = ts;

      ctx.clearRect(0, 0, SW, SVG_H);

      // ── Draw ping ripples ────────────────────────────────────────────────
      pingsRef.current = pingsRef.current.filter(p => {
        p.t += dt;
        const prog = p.t / 0.28;
        if (prog >= 1) return false;
        ctx.beginPath();
        ctx.arc(p.x, p.y, PEG_R + prog * 12, 0, Math.PI * 2);
        ctx.strokeStyle = `rgba(212,169,96,${0.55 * (1 - prog)})`;
        ctx.lineWidth = 1.5;
        ctx.stroke();
        return true;
      });

      // ── Update + draw balls ──────────────────────────────────────────────
      ballsRef.current = ballsRef.current.filter(ball => {
        if (ball.removed) return false;

        // Physics step
        ball.vy += GRAVITY * dt;
        ball.x  += ball.vx * dt;
        ball.y  += ball.vy * dt;

        // Peg row collisions (while-loop handles fast balls skipping rows)
        while (ball.nextRow < ROWS) {
          const pegY = TOP + ball.nextRow * RH;
          if (ball.y < pegY) break;

          const dir = ball.path[ball.nextRow] === 'R' ? 1 : -1;
          ball.vx = dir * IMPULSE;
          ball.vy *= VY_DAMP;

          // Ripple at collision point
          pingsRef.current.push({ x: ball.x, y: pegY, t: 0 });
          window.SoundFX?.pin();

          ball.nextRow++;
        }

        // Snap + finish when ball reaches slot area
        if (ball.y > SY + SH * 0.5 && !ball.done) {
          ball.done = true;
          ball.x  = ball.targetX;
          ball.vx = 0;
          ball.vy = 0;
          ball.onDone();
          setTimeout(() => { ball.removed = true; }, 350);
        }

        // ── Draw ball ──────────────────────────────────────────────────────
        // Soft glow
        const grd = ctx.createRadialGradient(ball.x, ball.y, 0, ball.x, ball.y, BALL_R * 2.8);
        grd.addColorStop(0, 'rgba(212,169,96,0.30)');
        grd.addColorStop(1, 'rgba(212,169,96,0)');
        ctx.beginPath();
        ctx.arc(ball.x, ball.y, BALL_R * 2.8, 0, Math.PI * 2);
        ctx.fillStyle = grd;
        ctx.fill();

        // Ball body
        ctx.beginPath();
        ctx.arc(ball.x, ball.y, BALL_R, 0, Math.PI * 2);
        ctx.fillStyle = '#d4a960';
        ctx.fill();
        ctx.strokeStyle = 'rgba(255,235,160,0.55)';
        ctx.lineWidth = 1.5;
        ctx.stroke();

        return !ball.removed;
      });

      rafId = requestAnimationFrame(loop);
    }

    rafId = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(rafId);
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  // ── Drop ─────────────────────────────────────────────────────────────────
  async function drop() {
    setErr('');
    const amt = parseInt(bet);
    if (!amt || amt <= 0) { setErr('Enter a valid bet'); return; }

    let data;
    try {
      data = await apiFetch('/api/game/plinko/play', { amount: amt });
    } catch (e) {
      setErr(e.message);
      return;
    }

    const id = nextId.current++;
    setInFlight(n => n + 1);

    ballsRef.current.push({
      id,
      x: CX + (Math.random() - 0.5) * 6,  // tiny random offset at top
      y: TOP - BALL_R - 2,
      vx: 0,
      vy: 0,
      path:    data.path,
      targetX: slotCX(data.slotIdx),
      nextRow: 0,
      done:    false,
      removed: false,
      onDone() {
        setInFlight(n => n - 1);
        setResults(prev => [
          { id, slotIdx: data.slotIdx, mult: data.mult, bet: amt, net: data.net },
          ...prev.slice(0, 49),
        ]);
        onBalanceChange(data.balance);
        if (data.net > 0) window.SoundFX?.win(); else window.SoundFX?.bust();
      },
    });
  }

  // ── Render ────────────────────────────────────────────────────────────────
  return (
    <div className="game-panel" data-game-view="plinko">
      <div className="game-panel-header">
        <button className="btn-back" onClick={onBack}>Lobby</button>
        <div>
          <h1 className="game-panel-title">Plinko</h1>
          <div className="game-panel-subtitle">·❦· The Cascade ·❦·</div>
        </div>
        <div className="game-panel-meta"><span>Up to 50×</span></div>
      </div>

      <div className="game-table">
        <FiligreeCorners />
        <div style={{ display: 'flex', justifyContent: 'center', overflowX: 'auto' }}>
          <div style={{ position: 'relative', width: SW, height: SVG_H, flexShrink: 0 }}>

            {/* Static layer — pegs + slots */}
            <svg
              width={SW} height={SVG_H}
              style={{ display: 'block', position: 'absolute', top: 0, left: 0 }}
            >
              {pegs.map((p, i) => (
                <circle key={i} cx={p.x} cy={p.y} r={PEG_R}
                  fill="rgba(212,169,96,0.28)" stroke="rgba(212,169,96,0.6)" strokeWidth={1} />
              ))}
              {Array.from({ length: SLOTS }, (_, k) => {
                const x = slotCX(k);
                return (
                  <g key={k}>
                    <rect
                      x={x - PS / 2 + 2} y={SY}
                      width={PS - 4} height={SH} rx={3}
                      fill="rgba(212,169,96,0.07)"
                      stroke="rgba(212,169,96,0.22)"
                      strokeWidth={1}
                    />
                    <text
                      x={x} y={SY + SH * 0.6}
                      textAnchor="middle"
                      fill={COLORS[k]}
                      fontSize={MULTS[k] >= 10 ? 9 : 10}
                      fontFamily="JetBrains Mono, monospace"
                      fontWeight="500"
                    >
                      {MULTS[k]}×
                    </text>
                  </g>
                );
              })}
            </svg>

            {/* Physics layer — balls + ripples on canvas */}
            <canvas
              ref={canvasRef}
              width={SW}
              height={SVG_H}
              style={{ display: 'block', position: 'absolute', top: 0, left: 0, pointerEvents: 'none' }}
            />
          </div>
        </div>
      </div>

      <div className="bet-controls">
        <span className="bet-label">Bet</span>
        <input
          className="bet-input" type="number" min={1} max={1000}
          value={bet} onChange={e => setBet(e.target.value)}
        />
        <div className="bet-presets">
          {[100, 250, 500, 1000].map(n => (
            <button key={n} className="btn-preset"
              onClick={() => { window.SoundFX?.chip(); setBet(n); }}>
              {n >= 1000 ? `${n / 1000}k` : n}
            </button>
          ))}
        </div>
        <div className="bet-increments">
          {[10, 25, 100].map(n => (
            <button key={n} className="btn-increment"
              onClick={() => { window.SoundFX?.chip(); setBet(Math.min(1000, (parseInt(bet)||0) + n)); }}>
              +{n}
            </button>
          ))}
        </div>
        <div className="action-buttons" style={{ alignItems: 'center' }}>
          <button
            className="btn btn-primary btn-lg"
            onClick={() => { window.SoundFX?.chip(); drop(); }}
          >
            Drop Ball
          </button>
          {inFlight > 0 && (
            <span style={{ fontSize: 10, letterSpacing: '0.18em', color: 'var(--gold-soft)', fontFamily: 'JetBrains Mono, monospace', whiteSpace: 'nowrap' }}>
              {inFlight} in air
            </span>
          )}
        </div>
      </div>
      {err && <p style={{ color: 'var(--garnet)', fontSize: 12, marginTop: 10 }}>{err}</p>}

      {results.length > 0 && (
        <div style={{ marginTop: 20 }}>
          <div className="section-header">
            <span className="section-title">Results</span>
            <div className="section-line" />
          </div>
          <div className="history-feed">
            {results.map(r => (
              <div key={r.id} className="history-item">
                <span className="history-game">Plinko</span>
                <span className="history-bet">Slot {r.slotIdx} · {r.mult}×</span>
                <span className={`history-result ${r.net > 0 ? 'pos' : r.net < 0 ? 'neg' : 'zero'}`}>
                  {r.net > 0 ? '+' : ''}{r.net.toLocaleString()} G
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}
