// game-baccarat.jsx — Banker / Player / Tie · 98.94% RTP on banker (~1.06% edge)
// Cards drawn i.i.d. uniform over 13 ranks (infinite-deck approximation).
// Tie bet RTP ~85.9% under this model (vs textbook 85.64% under 8-deck shoe).
// Banker/Player bets push on tie (player-favorable rule).
//
// §5.69 — server-resolved. Browser sends {bet, pick, clientSeed, coin, user}
// to /play/baccarat and receives {playerHand, bankerHand, pTotal, bTotal,
// outcome, payout, hex}. Server owns the full draw rules (initial 4 cards
// + player third-card rule + banker third-card rule). Browser animates the
// returned hands; payout is authoritative. Local `drawCard` + draw-rule
// logic removed — only `handTotal` (display) and `cardValue` (per-card
// rendering, indirectly via handTotal) remain.

const { useState: useStateB, useEffect: useEffectB, useRef: useRefB } = React;

// SUITS / RANKS were used by the local drawCard pre-§5.69. Server owns
// derivation now; we only need cardValue + handTotal for display.
function cardValue(r){ if (r==='A') return 1; if (['10','J','Q','K'].includes(r)) return 0; return +r; }
function handTotal(cards){ return cards.reduce((s,c)=>s+cardValue(c.r),0) % 10; }

function BaccaratPage({ palette: M, wallet, fairness, onConnect }) {
  const { bet, setBet, betStr, setBetStr } = useBetInput(0.1);
  const [pick, setPick] = useStateB('B');
  const [dealing, setDealing] = useStateB(false);
  const [player, setPlayer] = useStateB([]);
  const [banker, setBanker] = useStateB([]);
  const [last, setLast] = useStateB(null);
  const [history, setHistory] = useStateB([]);
  const [error, setError] = useBetError();
  // Synchronous re-entry lock (audit HIGH-9).
  const busyRef = useRefB(false);

  const MIN_BET = coinMinBet(wallet.activeCoin);
  // Per-coin max (30 SOL ≈ 5052.6 USDC/USDT).
  const MAX_BET_SOL = 30;
  const maxBet = maxBetForCoin(MAX_BET_SOL, wallet.activeCoin);

  // Clamp the bet into [MIN_BET, maxBet] when switching coins so USDC/USDT
  // (1-unit floor) don't leave the 0.1 SOL default below the minimum.
  useEffectB(() => {
    if (bet < MIN_BET) setBet(MIN_BET);
    else if (bet > maxBet) setBet(maxBet);
  }, [wallet.activeCoin]); // eslint-disable-line

  async function deal() {
    if (busyRef.current) return;
    if (dealing) return;
    if (!wallet.canPlay) { onConnect(); return; }
    const bag = wallet.balance[wallet.activeCoin] || 0;
    if (bet < MIN_BET) {
      setError(`minimum bet is ${MIN_BET} ${wallet.activeCoin}`);
      return;
    }
    if (bet > maxBet) {
      setError(`max bet on baccarat is ${maxBet.toFixed(2)} ${wallet.activeCoin}`);
      return;
    }
    if (bet > bag) {
      setError(`bet ${bet.toFixed(3)} ${wallet.activeCoin} exceeds bag (${bag.toFixed(3)} ${wallet.activeCoin} available)`);
      return;
    }
    busyRef.current = true;
    setError(null);
    setDealing(true); setLast(null); setPlayer([]); setBanker([]);
    if (!wallet.debit(wallet.activeCoin, bet)) {
      busyRef.current = false;
      setDealing(false);
      setError(`bag changed under the bet -- try again`);
      return;
    }

    // §5.69: server owns the full draw chain (initial 4 + player third-card
    // rule + banker third-card rule conditional on player's third). We send
    // {bet, pick, clientSeed, coin, user} and get back the two final hands
    // + outcome + payout. Browser animates the deal from the returned hands.
    let server;
    try {
      server = await callServerGame('/play/baccarat', {
        bet, pick,
        clientSeed: fairness.clientSeed,
        coin: wallet.activeCoin,
        user: wallet.username,
      });
    } catch (err) {
      wallet.credit(wallet.activeCoin, bet, 'baccarat-refund-' + Date.now());
      busyRef.current = false;
      setDealing(false);
      setError('server error: ' + (err && err.message ? err.message : String(err)));
      return;
    }

    const pFinal   = Array.isArray(server.playerHand) ? server.playerHand : [];
    const bFinal   = Array.isArray(server.bankerHand) ? server.bankerHand : [];
    const outcome  = String(server.outcome || 'T');
    const payout   = Number(server.payout) || 0;
    const serverHex = String(server.hex || '');

    // Animate dealing — play a hit sound as each card lands.
    for (let i = 0; i < pFinal.length; i++) {
      setTimeout(()=>{ setPlayer(prev=>[...prev, pFinal[i]]); playHitSound(); }, i*350);
    }
    for (let i = 0; i < bFinal.length; i++) {
      setTimeout(()=>{ setBanker(prev=>[...prev, bFinal[i]]); playHitSound(); }, i*350 + 200);
    }
    const totalDelay = Math.max(pFinal.length, bFinal.length) * 350 + 600;
    await new Promise(res=>setTimeout(res, totalDelay));

    const pTotal = handTotal(pFinal);
    const bTotal = handTotal(bFinal);

    if (payout > 0) wallet.credit(wallet.activeCoin, payout, serverHex || ('baccarat-cash-' + Date.now()));

    // Snapshot pick and bet so post-resolution edits to either don't repaint
    // the win/loss text colors (item 7 — user feedback).
    setLast({ pTotal, bTotal, outcome, payout, pick, bet, hex: serverHex.slice(0,12) });
    wallet.logPlay({ game:'Baccarat', bet, payout, win: payout>bet, coin: wallet.activeCoin, hex: serverHex.slice(0,16), outcome });
    // NOTE: server wrote /plays already (rules step9 blocks browser writes for Baccarat).
    setHistory(h=>[outcome, ...h].slice(0,20));
    setDealing(false);
    busyRef.current = false;
    // Winning a TIE bet pays 8:1 — fire the streak fanfare (same audio as a
    // 5×–9× slot hit) since it's a rare/celebratory outcome.
    if (pick === 'T' && outcome === 'T') {
      playStreakSound();
    } else {
      playOutcomeSound(payout > bet);
    }
  }

  return (
    <div className="rc-page" style={{flex:1, padding:'18px 26px 40px', overflowY:'auto'}}>
      <h1 className="rc-h-page" style={{fontSize:48, color:M.green, margin:'4px 0', textShadow:`3px 3px 0 ${M.hot}`}}>BACCARAT</h1>
      <div className="rc-h-sub" style={{color:M.ink2, fontSize:18, marginBottom:14}}>
        <span style={{color:M.green}}>98.94% RTP</span> on banker · <span style={{color:M.hot}}>1.06% edge</span> · max bet {maxBet.toFixed(2)} {wallet.activeCoin} (max win on tie {(maxBet*9).toFixed(2)})
      </div>

      <div className="rc-baccarat-playmode">
        <PlayModeBar palette={M} wallet={wallet} onConnect={onConnect}/>
      </div>
      <BetErrorToast error={error} onClose={()=>setError(null)} palette={M}/>

      <div className="rc-game-grid" style={{display:'grid', gridTemplateColumns:'320px 1fr', gap:18}}>
        <Frame title="bet" accent={M.yellow}>
          {/* Greyout the entire bet panel while dealing — pick + bet input
              + quick / ½ / 2× / max + DEAL all locked until the hand resolves. */}
          <div className="rc-bet-panel rc-baccarat-bet" style={{opacity: dealing ? 0.4 : 1, pointerEvents: dealing ? 'none' : 'auto', transition: 'opacity 0.15s'}}>
            <div className="rc-bc-pick-group">
              <div style={{fontSize:14, color:M.ink2, marginBottom:4}}>back</div>
              <div className="rc-bc-picks">
                {[
                  // odds derived from RTPs (§6 of HANDOFF): B wins ~45.86%,
                  // P wins ~44.61%, T ~9.54% (i.i.d. infinite-deck model).
                  {k:'B', l:'BANKER', sub:'1.95× (5% commission)', mobile:'45.9% · 1.95×', col:M.hot},
                  {k:'P', l:'PLAYER', sub:'2.0×',                   mobile:'44.6% · 2.0×',  col:M.cyan},
                  {k:'T', l:'TIE',    sub:'9.0× (rare)',            mobile:'9.5% · 9×',    col:M.yellow},
                ].map(o=>(
                  <button key={o.k} onClick={()=>setPick(o.k)} disabled={dealing} style={{
                    display:'block', width:'100%', background: pick===o.k?o.col:M.bg, color:pick===o.k?M.bg:o.col,
                    border:`2px solid ${o.col}`, fontFamily:'inherit', padding:'8px 12px', marginBottom:6,
                    cursor: dealing?'not-allowed':'pointer',
                    textAlign:'left', boxShadow:pick===o.k?`3px 3px 0 ${M.bg}`:'none',
                  }}>
                    <div style={{fontSize:18, fontWeight:700}}>{o.l}</div>
                    <div className="rc-bc-pick-sub" style={{fontSize:13, opacity:0.8}}>{o.sub}</div>
                    {/* Mobile-only compact line — `rc-bc-pick-sub` hides the
                        verbose desktop sub on mobile (§5.49), so this line
                        carries the odds + multiplier in a form that fits the
                        side-by-side 3-lane layout. */}
                    <div className="rc-bc-pick-mobile-sub" style={{fontSize:12, opacity:0.85, marginTop:2}}>{o.mobile}</div>
                  </button>
                ))}
              </div>
            </div>

            <div className="rc-bet-inputs">
              <div style={{fontSize:14, color:M.ink2, marginBottom:4, marginTop:12}}>bet ({wallet.activeCoin})</div>
              <input type="number" step={0.01} max={maxBet} value={betStr} onChange={e=>setBetStr(e.target.value)} disabled={dealing} style={{
                width:'100%', background:M.bg, border:`2px solid ${M.green}`, color:M.green, padding:'8px 10px', fontFamily:'inherit', fontSize:22, outline:'none',
              }}/>
              <div style={{marginTop:6}}>
                <QuickBetButtons current={bet} onPick={v=>setBet(v)} palette={M} disabled={dealing}/>
              </div>
              <div style={{display:'flex', gap:4, marginTop:6}}>
                <button onClick={()=>setBet(b=>Math.max(MIN_BET, +(b/2).toFixed(6)))} disabled={dealing} style={bcBtnSec(M)}>½</button>
                <button onClick={()=>setBet(b=>+Math.min(b*2, maxBet).toFixed(6))} disabled={dealing} style={bcBtnSec(M)}>2×</button>
                <button onClick={()=>setBet(+(Math.min(wallet.balance[wallet.activeCoin] || 0, maxBet)).toFixed(6))} disabled={dealing} title={`max bet: ${maxBet.toFixed(2)} ${wallet.activeCoin}`} style={bcBtnSec(M)}>max</button>
              </div>
            </div>

            <button className="rc-bet-action" onClick={deal} disabled={dealing} style={{
              marginTop:14, width:'100%', background:M.hot, color:M.bg, border:`3px solid ${M.bg}`, fontFamily:'inherit', fontSize:22, padding:'10px 0', fontWeight:700, cursor:dealing?'wait':'pointer', boxShadow:`5px 5px 0 ${M.green}`,
            }}>{dealing?'dealing…':'► DEAL'}</button>
          </div>
        </Frame>

        <div style={{display:'flex', flexDirection:'column', gap:14}}>
          <Frame title="the table" accent={M.green} className="rc-baccarat-table" style={{minHeight:300}}>
            <div className="rc-2col" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:18}}>
              <HandSide title="PLAYER" cards={player} total={handTotal(player)} accent={M.cyan} M={M}/>
              <HandSide title="BANKER" cards={banker} total={handTotal(banker)} accent={M.hot} M={M}/>
            </div>
            {/* Result area — always rendered with a fixed min-height so the
                table doesn't shift when a round finishes. flex-start anchors
                placeholder + result to the top so the visible text doesn't
                leap vertically when content swaps. */}
            <div className="rc-resolve-slot-col" style={{marginTop:18, borderTop:`2px dashed ${M.green}`, paddingTop:14, textAlign:'center', minHeight:96, display:'flex', flexDirection:'column', justifyContent:'flex-start'}}>
              {last && !dealing ? (
                <>
                  <div style={{fontSize:32, color: last.pick===last.outcome?M.green:M.red, fontWeight:700, textShadow:`2px 2px 0 ${M.bg}`}}>
                    {last.outcome==='B'?'BANKER WINS':last.outcome==='P'?'PLAYER WINS':'TIE'}
                  </div>
                  <div style={{fontSize:16, color:M.ink, marginTop:4}}>
                    Player {last.pTotal} vs Banker {last.bTotal} ·{' '}
                    <span style={{color: last.payout>0 ? M.green : M.red, fontWeight:700}}>
                      {last.payout>0 ? `+${last.payout.toFixed(2)}` : `−${last.bet.toFixed(2)}`} {wallet.activeCoin}
                    </span>
                  </div>
                  <div style={{fontSize:13, color:M.ink2, marginTop:4, fontFamily:'JetBrains Mono, monospace'}}>hash: {last.hex}…</div>
                </>
              ) : (
                <div style={{color:M.ink2, fontSize:14}}>
                  {dealing ? '> dealing the hand' : '> place your pick and deal'}
                </div>
              )}
            </div>
          </Frame>

          <Frame title="history" accent={M.cyan} className="rc-history rc-baccarat-history">
            <div style={{display:'flex', gap:4, flexWrap:'wrap'}}>
              {history.length===0 && <span style={{color:M.ink2, fontSize:14}}>no rounds yet</span>}
              {history.map((o,i)=>{
                const c = o==='B'?M.hot:o==='P'?M.cyan:M.yellow;
                return <div key={i} style={{width:24, height:24, borderRadius:'50%', background:c, color:M.bg, display:'flex', alignItems:'center', justifyContent:'center', fontSize:13, fontWeight:700}}>{o}</div>;
              })}
            </div>
          </Frame>
        </div>
      </div>

      <BaccaratStatsPanel palette={M} wallet={wallet}/>
      <HashPanel palette={M} fairness={fairness}/>
    </div>
  );
}

// Baccarat stats — derived from wallet.history filtered to game==='Baccarat'.
function BaccaratStatsPanel({ palette: M, wallet }) {
  const coin = wallet.activeCoin;
  const rounds = (wallet.history || []).filter(h => h.game === 'Baccarat' && h.coin === coin);
  const total = rounds.length;
  const wins = rounds.filter(r => r.win).length;
  const winRate = total ? (wins / total) * 100 : 0;
  const wagered = rounds.reduce((s, r) => s + (r.bet || 0), 0);
  const netPL = rounds.reduce((s, r) => s + ((r.payout || 0) - (r.bet || 0)), 0);
  const bestWin = rounds.reduce((m, r) => Math.max(m, (r.payout || 0) - (r.bet || 0)), 0);
  const tiesWon = rounds.filter(r => r.outcome === 'T' && r.win).length;
  const dp = 2;
  const fmt = n => `${n>=0?'+':''}${n.toFixed(dp)} ${coin}`;
  const fmtAbs = n => `${n.toFixed(dp)} ${coin}`;
  const tile = (label, value, color) => (
    <div style={{textAlign:'center', padding:'4px 0'}}>
      <div style={{fontSize:11, color:M.ink2, textTransform:'uppercase', letterSpacing:'0.05em'}}>{label}</div>
      <div style={{fontSize:22, color, fontWeight:700, lineHeight:1.1, marginTop:4, fontFamily:'JetBrains Mono, monospace'}}>{value}</div>
    </div>
  );
  return (
    <Frame title="baccarat stats" accent={M.cyan} className="rc-baccarat-stats" style={{marginTop:18}}>
      {total === 0 ? (
        <div style={{color:M.ink2, fontSize:14, padding:'4px 0'}}>{'>'} no rounds yet in {coin}. deal a hand.</div>
      ) : (
        <>
          <div className="rc-stat-6" style={{display:'grid', gridTemplateColumns:'repeat(6, 1fr)', gap:14}}>
            {tile('rounds',        total,                            M.green)}
            {tile('win rate',      `${winRate.toFixed(1)}%`,         M.cyan)}
            {tile('total wagered', fmtAbs(wagered),                  M.yellow)}
            {tile('net P/L',       fmt(netPL),                       netPL >= 0 ? M.green : M.red)}
            {tile('best win',      `+${bestWin.toFixed(dp)} ${coin}`,M.green)}
            {tile('ties won',      `${tiesWon}`,                     M.hot)}
          </div>
          <LastRoundsTable M={M} rounds={rounds} headers={['outcome', 'bet', 'payout', 'P/L']} renderRow={r => {
            const pl = (r.payout || 0) - (r.bet || 0);
            const plColor = pl > 0 ? M.green : pl === 0 ? M.yellow : M.red;
            const plText  = pl > 0 ? `+${pl.toFixed(dp)}` : pl === 0 ? '±0.00' : `−${Math.abs(pl).toFixed(dp)}`;
            const outcomeLabel = r.outcome === 'B' ? 'BANKER' : r.outcome === 'P' ? 'PLAYER' : 'TIE';
            const outcomeColor = r.outcome === 'B' ? M.hot : r.outcome === 'P' ? M.cyan : M.yellow;
            return [
              { value: outcomeLabel,                                       color: outcomeColor, fontWeight: 700 },
              { value: (r.bet || 0).toFixed(dp),                           color: M.ink },
              { value: r.payout > 0 ? (r.payout || 0).toFixed(dp) : '—',   color: r.payout > 0 ? M.green : M.ink2 },
              { value: plText,                                             color: plColor, fontWeight: 700 },
            ];
          }}/>
        </>
      )}
    </Frame>
  );
}

function bcBtnSec(M){ return { flex:1, background:M.bg, color:M.green, border:`1px solid ${M.green}`, fontFamily:'inherit', fontSize:14, padding:'4px 0', cursor:'pointer' }; }

function HandSide({ title, cards, total, accent, M }) {
  return (
    <div style={{textAlign:'center'}}>
      <div style={{fontSize:18, color:accent, marginBottom:8}}>{title}</div>
      <div style={{display:'flex', justifyContent:'center', gap:6, minHeight:100}}>
        {cards.map((c,i)=><Card key={i} card={c} M={M} accent={accent}/>)}
        {cards.length===0 && [0,1].map(i=><CardBack key={i} M={M}/>)}
      </div>
      <div style={{fontSize:24, color:M.green, marginTop:8, fontWeight:700}}>{cards.length>0 ? `total: ${total}` : '—'}</div>
    </div>
  );
}

function Card({ card, M, accent }) {
  const red = card.s==='♥' || card.s==='♦';
  return (
    <div style={{
      width:60, height:90, background:M.ink, color:red?'#cc0000':'#000',
      border:`2px solid ${accent}`, padding:6, position:'relative', fontFamily:'serif',
    }}>
      <style>{`@keyframes cardIn { from{transform:translateY(-30px) rotate(-10deg); opacity:0} to{transform:translateY(0) rotate(0); opacity:1} }`}</style>
      <div style={{position:'absolute', top:4, left:6, fontSize:14, fontWeight:700}}>{card.r}</div>
      <div style={{position:'absolute', bottom:4, right:6, fontSize:14, fontWeight:700, transform:'rotate(180deg)'}}>{card.r}</div>
      <div style={{position:'absolute', top:'50%', left:'50%', transform:'translate(-50%,-50%)', fontSize:24}}>{card.s}</div>
    </div>
  );
}

function CardBack({ M }) {
  return (
    <div style={{
      width:60, height:90, background: M.hot,
      border:`2px solid ${M.ink2}`,
    }}/>
  );
}

Object.assign(window, { BaccaratPage });
