// app.jsx — root, routes, layout, tweaks panel

const { useState: useStateApp, useEffect: useEffectApp } = React;

const APP_DEFAULTS = /*EDITMODE-BEGIN*/{
  "palette": "classic",
  "heroVariant": "pixel",
  "showChat": true,
  "showMarquee": true,
  "scanlines": false
}/*EDITMODE-END*/;

// Owner-only admin gate (HANDOFF §16). Wallet match is a UI-level only —
// rules-level enforcement (delete, /plays read) is UID-bound via Firebase
// Anonymous Auth, captured after first sign-in and hardcoded into the rules.
const OWNER_PUBKEY = 'BavzthzgkyBzKZVx9zuYKnJEdmw5PKX7zbx34pmG88RQ';

// Read ?page=… from the URL on first mount. Lets the owner type
// /?page=admin to land directly on the admin page even on devices without
// the gear icon visible. Only pages we recognize are honored.
const KNOWN_PAGES = new Set([
  'home','flip','slots','roulette','baccarat','blackjack','hilo','poker','mines',
  'fairness','how','leaderboard','profile','wallet','admin',
]);
function readInitialPage() {
  try {
    const p = new URLSearchParams(location.search).get('page');
    if (p && KNOWN_PAGES.has(p)) return p;
  } catch {}
  return 'home';
}

function App() {
  const [t, setT] = useTweaks(APP_DEFAULTS);
  const palette = usePalette(t.palette);
  const wallet = useWallet();
  const fairness = useFairness();
  const anonUid = useAnonAuth();
  const online = useOnlineCount();
  const sol = useSolPrice();
  const [page, setPage] = useStateApp(readInitialPage);
  const [walletModal, setWalletModal] = useStateApp(false);
  const [sidebarOpen, setSidebarOpen] = useStateApp(false);

  useEffectApp(() => { setSidebarOpen(false); }, [page]);

  // Per-channel chat. Each game gets its own room ('flip', 'slots', ...);
  // non-game pages share 'general'. Backed by Firebase Realtime Database
  // (see HANDOFF §5.58) — every device subscribed to the same channel sees
  // the same live message stream. Messages persist server-side; we read
  // the last 50 per channel.
  const GAME_CHANNELS = ['flip', 'slots', 'roulette', 'baccarat', 'blackjack', 'hilo', 'poker', 'mines'];
  // Presence channel mirrors the chat channel, with 'admin' bubbled up
  // separately so the per-channel breakdown shows the owner distinctly.
  const chatChannel = GAME_CHANNELS.includes(page) ? page : (page === 'admin' ? 'admin' : 'general');
  const chatMessages = useFirebaseChat(chatChannel);
  const addChatMessage = (text) => sendChatMessage(chatChannel, wallet.username, text);

  // Live presence — writes /presence/{uid} with current channel, refreshes
  // every 30s, clears on disconnect. Powers the real online count + the
  // admin Ops tab's per-channel breakdown.
  usePresence(anonUid, chatChannel);

  // Global client-side error capture → /errors/. Capped at 100 server-side
  // by the logClientError trim-then-push. Fires only after the anon UID is
  // available (rules require auth).
  useEffectApp(() => {
    if (!anonUid) return;
    const onErr = (event) => {
      const msg = event?.error?.stack || event?.message || String(event);
      logClientError({ msg, page: location.pathname + location.search });
    };
    const onRej = (event) => {
      const r = event?.reason;
      const msg = (r && (r.stack || r.message)) || (typeof r === 'string' ? r : 'unhandled rejection');
      logClientError({ msg: 'unhandled rejection: ' + msg, page: location.pathname + location.search });
    };
    window.addEventListener('error', onErr);
    window.addEventListener('unhandledrejection', onRej);
    return () => {
      window.removeEventListener('error', onErr);
      window.removeEventListener('unhandledrejection', onRej);
    };
  }, [anonUid]);

  // Close modal once connected
  useEffectApp(() => { if (wallet.connected) setWalletModal(false); }, [wallet.connected]);

  const M = palette;
  const onConnect = () => setWalletModal(true);
  const isOwner = !!wallet.address && wallet.address === OWNER_PUBKEY;

  // Game pages stay mounted at all times so:
  //  • Auto-resolving games (flip, slots, roulette, baccarat) keep running
  //    their setTimeouts in the background — if you click SPIN then navigate
  //    away, the round still completes, the balance updates, and the result
  //    is waiting when you come back.
  //  • Blackjack preserves its in-progress hand state across navigations —
  //    leave mid-player-phase and your hand is right where you left it when
  //    you return to the page.
  // Marketing / non-game pages mount on demand only (no persistent state to keep).

  return (
    <div className="rc-app-root" style={{height:'100vh', overflow:'hidden', background:M.bg, color:M.ink, display:'flex', flexDirection:'column'}}>
      {t.showMarquee && <Marquee palette={M}/>}
      <Topbar page={page} setPage={setPage} palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect} sol={sol} onMenuClick={()=>setSidebarOpen(o=>!o)} sidebarOpen={sidebarOpen} isOwner={isOwner} chatHidden={!t.showChat} onShowChat={()=>setT('showChat', true)}/>

      <div className="rc-app-body" style={{flex:1, display:'flex', overflow:'hidden', minHeight:0, position:'relative'}}>
        <Sidebar page={page} setPage={setPage} palette={M} wallet={wallet} mobileOpen={sidebarOpen} onClose={()=>setSidebarOpen(false)}/>
        <main className="rc-app-main" style={{flex:1, display:'flex', flexDirection:'column', overflow:'hidden', minWidth:0}}>
          {/* Game pages — always mounted; only the active one is visible. */}
          <GameMount visible={page === 'flip'}>
            <FlipPage palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect} heroVariant={t.heroVariant}/>
          </GameMount>
          <GameMount visible={page === 'slots'}>
            <SlotsPage palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect}/>
          </GameMount>
          <GameMount visible={page === 'roulette'}>
            <RoulettePage palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect}/>
          </GameMount>
          <GameMount visible={page === 'baccarat'}>
            <BaccaratPage palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect}/>
          </GameMount>
          <GameMount visible={page === 'blackjack'}>
            <BlackjackPage palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect}/>
          </GameMount>
          <GameMount visible={page === 'hilo'}>
            <HiloPage palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect}/>
          </GameMount>
          <GameMount visible={page === 'poker'}>
            <VideoPokerPage palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect}/>
          </GameMount>
          <GameMount visible={page === 'mines'}>
            <MinesPage palette={M} wallet={wallet} fairness={fairness} onConnect={onConnect}/>
          </GameMount>

          {/* Non-game pages — render only the active one. */}
          {page === 'home'        && <HomePage setPage={setPage} palette={M} wallet={wallet} fairness={fairness} online={online}/>}
          {page === 'fairness'    && <FairnessPage palette={M} wallet={wallet} fairness={fairness}/>}
          {page === 'how'         && <HowPage setPage={setPage} palette={M}/>}
          {page === 'leaderboard' && <LeaderboardPage palette={M} online={online} wallet={wallet}/>}
          {page === 'profile'     && <ProfilePage palette={M} wallet={wallet} setPage={setPage} fairness={fairness} onConnect={onConnect}/>}
          {page === 'wallet'      && <WalletPage palette={M} wallet={wallet} setPage={setPage} onConnect={onConnect}/>}
        </main>
        {t.showChat && <ChatSidebar palette={M} channel={chatChannel} messages={chatMessages} onSend={addChatMessage} me={wallet.username} onHide={()=>setT('showChat', false)}/>}
      </div>

      {t.scanlines && <CrtOverlay palette={M}/>}

      {walletModal && <WalletModal wallet={wallet} onClose={()=>setWalletModal(false)}/>}

      {/* Admin dashboard takes over the screen as a full-bleed overlay (§5.61).
          The casino chrome (topbar, sidebar, chat) and the GameMount tree stay
          mounted underneath — autospins / blackjack hands / fairness chain all
          keep running, so the owner can dip in/out without losing state. The
          overlay sits above scanlines + tweaks but below the wallet modal so
          a connect prompt fired from inside admin still surfaces correctly. */}
      {page === 'admin' && (
        <AdminPage palette={M} wallet={wallet} isOwner={isOwner} anonUid={anonUid} setPage={setPage}/>
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection title="palette">
          <TweakSelect label="theme" value={t.palette} onChange={v=>setT('palette', v)}
            options={Object.entries(MEME_PALETTES).map(([k,v])=>({value:k, label:v.name}))}/>
          <div style={{display:'grid', gridTemplateColumns:'repeat(3,1fr)', gap:6, marginTop:8}}>
            {Object.entries(MEME_PALETTES).map(([k,p])=>(
              <button key={k} onClick={()=>setT('palette', k)} style={{
                background:p.bg, color:p.ink, border:`2px solid ${t.palette===k?p.green:'#444'}`,
                padding:'8px 4px', cursor:'pointer', fontFamily:'inherit', fontSize:11, lineHeight:1.2,
                boxShadow: t.palette===k ? `3px 3px 0 ${p.green}` : 'none', textAlign:'left',
              }}>
                <div style={{display:'flex', gap:2, marginBottom:4}}>
                  <span style={{width:14, height:14, background:p.green}}/>
                  <span style={{width:14, height:14, background:p.hot}}/>
                  <span style={{width:14, height:14, background:p.yellow}}/>
                  <span style={{width:14, height:14, background:p.cyan}}/>
                </div>
                <div style={{color:p.green, fontSize:11}}>{p.name}</div>
              </button>
            ))}
          </div>
        </TweakSection>

        <TweakSection title="coin visual (flip game)">
          <TweakRadio label="hero variant" value={t.heroVariant} onChange={v=>setT('heroVariant', v)}
            options={[{value:'pixel', label:'pixel'}, {value:'sleek', label:'sleek'}, {value:'meme', label:'meme'}]}/>
        </TweakSection>

        <TweakSection title="chrome">
          <TweakToggle label="chat sidebar" value={t.showChat} onChange={v=>setT('showChat', v)}/>
          <TweakToggle label="marquee strip" value={t.showMarquee} onChange={v=>setT('showMarquee', v)}/>
          <TweakToggle label="CRT scanlines" value={t.scanlines} onChange={v=>setT('scanlines', v)}/>
        </TweakSection>

        <TweakSection title="dev/demo">
          <TweakButton label="+ 1 SOL (demo)" onClick={()=>wallet.credit('SOL', 1)}/>
          <TweakButton label="reset bag" onClick={()=>{ wallet.setActiveCoin('SOL'); }}/>
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

// GameMount — keeps a game page mounted in the DOM regardless of navigation.
// Hidden pages get display:none (zero layout impact) but their React state
// survives, their setTimeouts keep firing, and their wallet credits/debits
// still flow. This is the mechanism that lets slots/roulette/flip/baccarat
// auto-resolve while you're on a different page, and lets blackjack pause
// mid-hand and pick up exactly where you left it.
function GameMount({ visible, children }) {
  return (
    <div style={{flex:1, display: visible ? 'flex' : 'none', flexDirection:'column', overflow:'hidden', minHeight:0, minWidth:0}}>
      {children}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
