/* ── Crash ───────────────────────────────────────────────────────────────── */

function calcMultiplier(elapsedMs) {
  return parseFloat(Math.max(1, Math.pow(Math.E, 0.1 * elapsedMs / 1000)).toFixed(2));
}

function Crash({ balance, apiFetch, onBack, onBalanceChange }) {
  const { useState, useEffect, useRef } = React;

  const [betAmount, setBetAmount]     = useState(100);
  const [autoCashout, setAutoCashout] = useState('');
  const [phase, setPhase]             = useState('idle');   // idle | countdown | running | crashed
  const [multiplier, setMultiplier]   = useState(1.00);
  const [countdown, setCountdown]     = useState(null);
  const [cashingOut, setCashingOut]   = useState(false);
  const [cashedOutAt, setCashedOutAt] = useState(null);  // mult when player cashed out, game still running
  const [result, setResult]           = useState(null);
  const [history, setHistory]         = useState([]);
  const [err, setErr]                 = useState('');

  const rafRef            = useRef(null);
  const pollRef           = useRef(null);
  const betIdRef          = useRef(null);
  const betAmtRef         = useRef(100);
  const startTimeRef      = useRef(null);
  const phaseRef          = useRef('idle');
  const canvasRef         = useRef(null);
  const pointsRef         = useRef([]);
  const cashoutLineRef    = useRef(null); // mult where player cashed out, for canvas line
  const cashedOutMultRef  = useRef(null); // set after cashout confirmed, used by poll to call finishGame(won)
  const finishedRef       = useRef(false);
  const acSentRef         = useRef(false); // prevent double auto-cashout fire

  // ── Canvas ────────────────────────────────────────────────────────────────
  function drawCanvas(crashed) {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext('2d');
    const W = canvas.width, H = canvas.height;
    const pts = pointsRef.current;
    ctx.clearRect(0, 0, W, H);
    if (pts.length < 2) return;

    const maxT = pts[pts.length - 1].t;
    const maxM = Math.max(...pts.map(p => p.m), 2);
    const padL = 36, padR = 16, padT = 16, padB = 28;
    const W2 = W - padL - padR, H2 = H - padT - padB;

    function toC(t, m) {
      return {
        x: padL + (t / maxT) * W2,
        y: H - padB - ((m - 1) / Math.max(maxM - 1, 0.5)) * H2,
      };
    }

    ctx.setLineDash([3, 6]);
    ctx.lineWidth = 1;
    for (let m = 2; m <= Math.ceil(maxM); m++) {
      const { y } = toC(0, m);
      ctx.strokeStyle = 'rgba(212,169,96,0.09)';
      ctx.beginPath(); ctx.moveTo(padL, y); ctx.lineTo(W - padR, y); ctx.stroke();
      ctx.fillStyle = 'rgba(139,149,168,0.55)';
      ctx.font = '10px JetBrains Mono, monospace';
      ctx.textAlign = 'right';
      ctx.fillText(`${m}×`, padL - 4, y + 4);
    }
    ctx.setLineDash([]);

    const color      = crashed ? 'rgba(213,106,106,1)' : 'rgba(111,185,106,1)';
    const colorAlpha = crashed ? 'rgba(213,106,106,' : 'rgba(111,185,106,';

    const first = toC(pts[0].t, pts[0].m);
    const last  = toC(pts[pts.length - 1].t, pts[pts.length - 1].m);
    const grad  = ctx.createLinearGradient(0, padT, 0, H - padB);
    grad.addColorStop(0, colorAlpha + '0.18)');
    grad.addColorStop(1, colorAlpha + '0)');
    ctx.beginPath();
    pts.forEach((p, i) => {
      const { x, y } = toC(p.t, p.m);
      if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
    });
    ctx.lineTo(last.x, H - padB);
    ctx.lineTo(first.x, H - padB);
    ctx.closePath();
    ctx.fillStyle = grad;
    ctx.fill();

    ctx.beginPath();
    pts.forEach((p, i) => {
      const { x, y } = toC(p.t, p.m);
      if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
    });
    ctx.strokeStyle = color;
    ctx.lineWidth = 2.5;
    ctx.shadowColor = color;
    ctx.shadowBlur = 10;
    ctx.stroke();
    ctx.shadowBlur = 0;

    ctx.beginPath();
    ctx.arc(last.x, last.y, 5, 0, Math.PI * 2);
    ctx.fillStyle = color;
    ctx.shadowColor = color;
    ctx.shadowBlur = 16;
    ctx.fill();
    ctx.shadowBlur = 0;

    const cm = cashoutLineRef.current;
    if (cm && cm >= 1 && maxM > 1) {
      const yLine = H - padB - ((cm - 1) / Math.max(maxM - 1, 0.5)) * H2;
      ctx.setLineDash([5, 5]);
      ctx.strokeStyle = 'rgba(212,169,96,0.75)';
      ctx.lineWidth = 1.5;
      ctx.beginPath(); ctx.moveTo(padL, yLine); ctx.lineTo(W - padR, yLine); ctx.stroke();
      ctx.setLineDash([]);
      ctx.fillStyle = 'rgba(212,169,96,0.9)';
      ctx.font = 'bold 10px JetBrains Mono, monospace';
      ctx.textAlign = 'left';
      ctx.fillText(`✓ ${cm.toFixed(2)}×`, padL + 6, yLine - 5);
    }
  }

  // ── Lifecycle helpers ─────────────────────────────────────────────────────
  function stopAll() {
    if (rafRef.current) { cancelAnimationFrame(rafRef.current); rafRef.current = null; }
    if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; }
    window.SoundFX?.riseStop();
  }

  // Called once when the game definitively ends (crash or cashout confirmed)
  function finishGame({ won, cashoutAt, crashedAt }) {
    if (finishedRef.current) return;
    finishedRef.current = true;
    stopAll();

    const amt  = betAmtRef.current;
    const last = pointsRef.current[pointsRef.current.length - 1];
    const finalM = crashedAt ?? last?.m ?? 1;
    pointsRef.current.push({ t: (last?.t ?? 0) + 10, m: finalM });
    drawCanvas(true);
    setMultiplier(finalM);
    setPhase('crashed');
    phaseRef.current = 'crashed';
    window.SoundFX?.crash();
    setCashingOut(false);
    setCashedOutAt(null);

    if (won) {
      cashoutLineRef.current = cashoutAt;
      const win = Math.floor(amt * cashoutAt);
      const net = win - amt;
      setResult({ won: true, net, cashedAt: cashoutAt, crashAt: crashedAt });
      setHistory(h => [{ crashAt: crashedAt, cashedAt: cashoutAt, won: true, net }, ...h.slice(0, 29)]);
      setTimeout(() => window.SoundFX?.win(), 500);
    } else {
      setResult({ won: false, net: -amt, crashAt: crashedAt });
      setHistory(h => [{ crashAt: crashedAt, cashedAt: null, won: false, net: -amt }, ...h.slice(0, 29)]);
    }
  }

  // Polls server every 200 ms — server detects crash by comparing elapsed time
  // to crash point (which never leaves the server)
  function startPoll(betId) {
    pollRef.current = setInterval(async () => {
      if (phaseRef.current !== 'running') return;
      try {
        const data = await apiFetch('/api/game/crash/poll', { betId });
        if (!data.active) {
          clearInterval(pollRef.current);
          pollRef.current = null;
          if (data.reason === 'not_found') return; // stale poll, game was already ended elsewhere
          if (data.cashedOut) {
            // Player already cashed out — now we know the natural crash point
            finishGame({ won: true, cashoutAt: cashedOutMultRef.current, crashedAt: data.crashedAt });
          } else {
            finishGame({ won: false, cashoutAt: null, crashedAt: data.crashedAt });
          }
        }
      } catch (_) {}
    }, 200);
  }

  // ── Cash out ──────────────────────────────────────────────────────────────
  async function cashOut() {
    if (phaseRef.current !== 'running' || cashingOut || cashedOutMultRef.current) return;
    setCashingOut(true);
    window.SoundFX?.chip();
    try {
      const data = await apiFetch('/api/game/crash/cashout', { betId: betIdRef.current });
      if (data.won) {
        cashedOutMultRef.current = data.multiplier;
        cashoutLineRef.current   = data.multiplier;
        onBalanceChange(data.balance);
        setCashedOutAt(data.multiplier);
        setCashingOut(false);
        setTimeout(() => window.SoundFX?.win(), 100);
        // Keep RAF + poll running — the poll will call finishGame when the game naturally crashes
      } else {
        // Cashed out after the crash point — server says we lost
        finishGame({ won: false, cashoutAt: null, crashedAt: data.crashedAt });
      }
    } catch (e) {
      setCashingOut(false);
      setErr(e.message);
    }
  }

  // ── Start ─────────────────────────────────────────────────────────────────
  async function startGame() {
    setErr('');
    const amt = parseInt(betAmount);
    if (!amt || amt <= 0) { setErr('Enter a valid bet'); return; }
    if (amt > balance)    { setErr('Insufficient balance'); return; }

    stopAll();
    finishedRef.current     = false;
    acSentRef.current       = false;
    cashoutLineRef.current  = null;
    cashedOutMultRef.current = null;
    betAmtRef.current       = amt;
    pointsRef.current       = [];
    setCashedOutAt(null);

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

    onBalanceChange(data.balance);
    const { betId } = data; // crash point stays on server — not sent to client
    betIdRef.current = betId;

    const ac = parseFloat(autoCashout);
    setResult(null);
    setCashingOut(false);
    setMultiplier(1.00);
    phaseRef.current = 'countdown';

    const canvas = canvasRef.current;
    if (canvas) canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);

    for (let i = 3; i >= 1; i--) {
      setCountdown(i);
      setPhase('countdown');
      window.SoundFX?.countdownBeep(i);
      await new Promise(r => setTimeout(r, 950));
    }
    setCountdown(null);
    setPhase('running');
    phaseRef.current = 'running';
    window.SoundFX?.riseStart();

    apiFetch('/api/game/crash/launch', { betId }).catch(console.error);

    const start = Date.now();
    startTimeRef.current = start;

    startPoll(betId);

    function loop() {
      if (phaseRef.current !== 'running') return;
      const elapsed = Date.now() - start;
      const m = calcMultiplier(elapsed);

      setMultiplier(m);
      pointsRef.current.push({ t: elapsed, m });
      drawCanvas(false);
      window.SoundFX?.riseUpdate(m);

      // Auto-cashout: trigger when multiplier reaches the target
      if (!acSentRef.current && ac >= 1.01 && m >= ac) {
        acSentRef.current = true;
        setCashingOut(true);
        window.SoundFX?.chip();
        apiFetch('/api/game/crash/cashout', { betId }).then(d => {
          if (d.won) {
            cashedOutMultRef.current = d.multiplier;
            cashoutLineRef.current   = d.multiplier;
            onBalanceChange(d.balance);
            setCashedOutAt(d.multiplier);
            setCashingOut(false);
            setTimeout(() => window.SoundFX?.win(), 100);
            // Keep running — poll will call finishGame when game naturally crashes
          } else {
            // Crashed before auto-cashout fired — poll will detect it too, but handle here in case
            finishGame({ won: false, cashoutAt: null, crashedAt: d.crashedAt });
          }
        }).catch(() => setCashingOut(false));
      }

      rafRef.current = requestAnimationFrame(loop);
    }

    rafRef.current = requestAnimationFrame(loop);
  }

  useEffect(() => () => stopAll(), []);

  const isRunning = phase === 'running';
  const isBusy    = phase === 'countdown' || phase === 'running';
  const isCrashed = phase === 'crashed';
  const multColor = isCrashed ? 'var(--garnet)' : 'var(--emerald)';

  return (
    <div className="game-panel" data-game-view="crash">
      <div className="game-panel-header">
        <button className="btn-back" onClick={onBack}>Lobby</button>
        <div>
          <h1 className="game-panel-title">Crash</h1>
          <div className="game-panel-subtitle">·❦· The Rising Star ·❦·</div>
        </div>
        <div className="game-panel-meta"><span>Up to 100×</span></div>
      </div>

      <div className="game-table" style={{ padding: '20px 20px 16px' }}>
        <FiligreeCorners />
        <div style={{ textAlign: 'center', minHeight: 90, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', marginBottom: 10 }}>
          {phase === 'countdown' ? (
            <div key={countdown} className="crash-countdown">{countdown}</div>
          ) : (
            <>
              <div className="crash-multiplier" style={{ color: multColor }}>
                {multiplier.toFixed(2)}×
              </div>
              {isCrashed && (
                <div style={{ fontSize: 11, letterSpacing: '0.28em', textTransform: 'uppercase', color: 'var(--garnet)', marginTop: 4 }}>
                  Crashed
                </div>
              )}
              {isRunning && cashedOutAt && (
                <div style={{ fontSize: 13, letterSpacing: '0.06em', color: 'var(--emerald)', marginTop: 4, fontFamily: 'Cormorant Garamond, serif', fontWeight: 700 }}>
                  ✓ Cashed out at {cashedOutAt.toFixed(2)}×
                </div>
              )}
              {isRunning && cashingOut && !cashedOutAt && (
                <div style={{ fontSize: 11, letterSpacing: '0.18em', textTransform: 'uppercase', color: 'var(--gold)', marginTop: 4 }}>
                  Cashing out…
                </div>
              )}
            </>
          )}
        </div>

        <div className="crash-graph-wrap">
          <canvas ref={canvasRef} width={600} height={200} className="crash-canvas" />
        </div>

        {isCrashed && result && (
          <div className={`game-status ${result.won ? 'win' : 'loss'}`} style={{ marginTop: 10 }}>
            {result.won
              ? `Cashed at ${result.cashedAt.toFixed(2)}×${result.crashAt ? ` — crashed ${result.crashAt.toFixed(2)}×` : ''} — +${result.net.toLocaleString()} G`
              : `Crashed at ${result.crashAt?.toFixed(2) ?? '?'}× — ${result.net.toLocaleString()} G`}
          </div>
        )}
      </div>

      <div className="bet-controls">
        <span className="bet-label">Bet</span>
        <input
          className="bet-input"
          type="number"
          min={1}
          max={1000}
          value={betAmount}
          onChange={e => setBetAmount(e.target.value)}
          disabled={isBusy}
        />
        <div className="bet-presets">
          {[100, 250, 500, 1000].map(n => (
            <button key={n} className="btn-preset" onClick={() => { window.SoundFX?.chip(); setBetAmount(n); }} disabled={isBusy}>
              {n >= 1000 ? `${n/1000}k` : n}
            </button>
          ))}
        </div>
        <div className="bet-increments">
          {[10, 25, 100].map(n => (
            <button key={n} className="btn-increment" disabled={isBusy}
              onClick={() => { window.SoundFX?.chip(); setBetAmount(Math.min(1000, (parseInt(betAmount)||0) + n)); }}>
              +{n}
            </button>
          ))}
        </div>
        <span className="bet-label" style={{ marginLeft: 6 }}>Auto</span>
        <input
          className="bet-input"
          type="number"
          step="0.1"
          min="1.1"
          placeholder="off"
          value={autoCashout}
          onChange={e => setAutoCashout(e.target.value)}
          disabled={isBusy}
          style={{ width: 86 }}
        />
        <div className="action-buttons">
          {!isBusy && (
            <button className="btn btn-primary btn-lg" onClick={() => { window.SoundFX?.chip(); startGame(); }}>
              {isCrashed ? 'Play Again' : 'Launch'}
            </button>
          )}
          {phase === 'countdown' && (
            <button className="btn btn-lg" disabled>Launching…</button>
          )}
          {isRunning && !cashingOut && !cashedOutAt && (
            <button className="crash-cashout-btn" onClick={cashOut}>
              Cash Out &nbsp;<span style={{ fontFamily: 'JetBrains Mono, monospace' }}>{multiplier.toFixed(2)}×</span>
            </button>
          )}
          {isRunning && cashingOut && !cashedOutAt && (
            <button className="btn btn-lg" disabled style={{ color: 'var(--gold)', borderColor: 'var(--gold-deep)' }}>
              Cashing Out…
            </button>
          )}
          {isRunning && cashedOutAt && (
            <button className="btn btn-lg" disabled style={{ color: 'var(--emerald)', borderColor: 'var(--emerald)', opacity: 0.7 }}>
              Won — watching crash…
            </button>
          )}
        </div>
      </div>
      {err && <p style={{ color: 'var(--garnet)', fontSize: 12, marginTop: 10 }}>{err}</p>}

      {history.length > 0 && (
        <div style={{ marginTop: 20 }}>
          <div className="section-header">
            <span className="section-title">Recent Crashes</span>
            <div className="section-line" />
          </div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
            {history.map((h, i) => (
              <div key={i} className="crash-history-chip" style={{ color: h.won ? 'var(--emerald)' : 'var(--garnet)' }}>
                <span className="crash-chip-crash">{h.crashAt?.toFixed(2) ?? '—'}×</span>
                {h.won && <span className="crash-chip-out">out {h.cashedAt.toFixed(2)}×</span>}
                <span className="crash-chip-net" style={{ color: h.net > 0 ? 'var(--emerald)' : 'var(--garnet)' }}>
                  {h.net > 0 ? '+' : ''}{h.net.toLocaleString()}
                </span>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}
