// The main Game component — state machine, interactions, AI runner
const { useState: uS, useEffect: uE, useRef: uR, useMemo: uM, useCallback: uC } = React;

function Game({ config, onQuit, settings, setSettings, mpMode, mpConn }) {
  const [state, setState] = uS(() => window.makeInitialGameState(config));
  const [selected, setSelected] = uS(new Set());
  const [staged, setStaged] = uS([]);
  const [toasts, pushToast] = window.useToasts();
  const [showHelp, setShowHelp] = uS(false);
  const [showTweaks, setShowTweaks] = uS(false);
  const [aiThinking, setAiThinking] = uS(false);
  const [pulsingPile, setPulsingPile] = uS(null);
  const [statusFlash, setStatusFlash] = uS(null);
  const [drawnAnim, setDrawnAnim] = uS(null);
  const [drawnCardId, setDrawnCardId] = uS(null);
  const [victory, setVictory] = uS(null);
  const [mpSynced, setMpSynced] = uS(false);
  const [mpDisconnected, setMpDisconnected] = uS(false);
  const stateRef = uR(state);
  stateRef.current = state;
  const handRef = uR(null);

  function buildGuestView(s) {
    return {
      ...s,
      player: { ...s.ai },
      ai: {
        ...s.player,
        hand: s.player.hand.map((_, i) => ({ id: `_h${i}`, hidden: true, joker: false, color: 'black' })),
      },
      turn: s.turn === 'player' ? 'ai' : s.turn === 'ai' ? 'player' : s.turn,
      winner: s.winner === 'player' ? 'ai' : s.winner === 'ai' ? 'player' : s.winner,
      melds: s.melds.map(m => ({ ...m, owner: m.owner === 'you' ? 'ai' : 'you' })),
    };
  }

  // Drag-and-drop
  const canInteract = () => state.turn === 'player' && state.phase === 'play' && !victory;
  const dnd = window.useCardDrag({
    canInteract,
    onReorder: (fromId, toId) => {
      setState(s => {
        const hand = [...s.player.hand];
        const fromIdx = hand.findIndex(c => c.id === fromId);
        const toIdx = hand.findIndex(c => c.id === toId);
        if (fromIdx < 0 || toIdx < 0) return s;
        const [moved] = hand.splice(fromIdx, 1);
        const insertAt = hand.findIndex(c => c.id === toId);
        hand.splice(insertAt, 0, moved);
        return { ...s, player: { ...s.player, hand } };
      });
    },
    onStage: (id) => {
      if (!staged.includes(id)) setStaged([...staged, id]);
      selected.delete(id); setSelected(new Set(selected));
    },
    onUnstage: (id) => {
      setStaged(staged.filter(x => x !== id));
    },
    onDiscardDrop: (id) => {
      // Trigger discard of this specific card
      if (state.phase !== 'play') return;
      setStaged(staged.filter(x => x !== id));
      // Discard via existing logic
      doDiscard(id);
    }
  });

  // AI intro line
  uE(() => {
    if (settings.trashTalk) pushToast({ kind: 'ai', who: config.personality.toUpperCase(), text: window.RomeAI.randLine(config.personality, 'greet') });
    else pushToast({ kind: 'system', text: 'Hand dealt · Your turn' });
    sfx('start');
  // eslint-disable-next-line
  }, []);

  // Scroll hand to center the newly drawn card (direct scrollLeft — works on iOS Safari)
  uE(() => {
    if (!drawnCardId) return;
    // Small delay lets the browser finish laying out the newly added card
    const t = setTimeout(() => {
      const container = handRef.current;
      if (!container) return;
      const el = container.querySelector(`[data-card-id="${drawnCardId}"]`);
      if (el) {
        const { left: cLeft, width: cWidth } = container.getBoundingClientRect();
        const { left: eLeft, width: eWidth } = el.getBoundingClientRect();
        container.scrollLeft += eLeft - cLeft - (cWidth - eWidth) / 2;
      }
      setTimeout(() => setDrawnCardId(null), 1000);
    }, 80);
    return () => clearTimeout(t);
  }, [drawnCardId]);

  // Sound helper — respects settings.sound
  function sfx(name) {
    if (settings.sound && window.RomeAudio) window.RomeAudio.play(name);
  }

  // Keep audio enabled state synced with settings
  uE(() => {
    if (window.RomeAudio) window.RomeAudio.setEnabled(!!settings.sound);
  }, [settings.sound]);

  // Prime audio on first interaction
  uE(() => {
    function prime() { if (window.RomeAudio) window.RomeAudio.prime(); window.removeEventListener('pointerdown', prime); }
    window.addEventListener('pointerdown', prime);
    return () => window.removeEventListener('pointerdown', prime);
  }, []);

  // Keyboard shortcuts — Esc clears staging/selection
  uE(() => {
    function onKey(e) {
      if (e.key === 'Escape') {
        if (staged.length) setStaged([]);
        else if (selected.size) setSelected(new Set());
      }
    }
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [staged.length, selected.size]);

  // Apply theme
  uE(() => {
    const t = window.TABLE_THEMES[settings.tableTheme] || window.TABLE_THEMES.violet;
    document.documentElement.style.setProperty('--felt', t.felt);
    document.documentElement.style.setProperty('--felt-2', t.felt2);
  }, [settings.tableTheme]);

  // --- Helpers ---
  const handMap = uM(() => {
    const m = new Map();
    for (const c of state.player.hand) m.set(c.id, c);
    return m;
  }, [state.player.hand]);

  const stagedCards = uM(() => staged.map(id => handMap.get(id)).filter(Boolean), [staged, handMap]);
  const stagedMeldStatus = uM(() => {
    if (stagedCards.length < 3) return { ok: false, reason: 'Pick 3+ cards' };
    const ok = window.RomeEngine.isValidMeld(stagedCards);
    if (!ok) return { ok: false, reason: 'Not a valid meld' };
    const pts = window.RomeEngine.meldPoints(stagedCards);
    if (settings.requireOpen40 && !state.player.hasOpenedMeld) {
      const withPending = state.player.openingPoints + pts;
      if (withPending < 40) return { ok: true, warn: `${withPending}/40 points to open`, points: pts };
    }
    return { ok: true, points: pts };
  }, [stagedCards, settings.requireOpen40, state.player.hasOpenedMeld, state.player.openingPoints]);

  // Cards available for layoff: staged takes priority, else currently selected
  const layoffCandidates = uM(() => {
    if (staged.length > 0) return stagedCards;
    if (selected.size > 0) return [...selected].map(id => handMap.get(id)).filter(Boolean);
    return [];
  }, [staged, stagedCards, selected, handMap]);

  const canLayoffSomewhere = uM(() => {
    if (!settings.layoffOpponent || layoffCandidates.length === 0) return false;
    if (state.turn !== 'player' || state.phase !== 'play' || state.winner) return false;
    if (settings.requireOpen40 && !state.player.hasOpenedMeld) return false;
    return state.melds.some(m => window.RomeEngine.isValidMeld([...m.cards, ...layoffCandidates]));
  }, [settings.layoffOpponent, layoffCandidates, state.turn, state.phase, state.winner,
      settings.requireOpen40, state.player.hasOpenedMeld, state.melds]);

  // Valid meld from current selection (no staging needed)
  const selectedMeldStatus = uM(() => {
    if (!canPlay || selected.size < 3 || staged.length > 0) return null;
    const cards = [...selected].map(id => handMap.get(id)).filter(Boolean);
    if (!window.RomeEngine.isValidMeld(cards)) return null;
    const pts = window.RomeEngine.meldPoints(cards);
    if (settings.requireOpen40 && !state.player.hasOpenedMeld) {
      const total = state.player.openingPoints + pts;
      return { points: pts, warn: total < 40 ? `${total}/40pts` : null };
    }
    return { points: pts };
  }, [canPlay, selected, handMap, staged.length, settings.requireOpen40,
      state.player.hasOpenedMeld, state.player.openingPoints]);

  // ---- Guest action senders (function declarations for hoisting) ----
  function guestDrawDeck() {
    if (state.turn !== 'player' || state.phase !== 'draw') return;
    mpConn.send({ type: 'drawDeck' });
  }
  function guestDrawDiscard() {
    if (state.turn !== 'player' || state.phase !== 'draw') return;
    if (!state.discard.length) return;
    mpConn.send({ type: 'drawDiscard' });
  }
  function guestLayMeld() {
    if (state.turn !== 'player' || state.phase !== 'play') return;
    if (!stagedMeldStatus.ok) { flashStatus('Invalid meld'); return; }
    mpConn.send({ type: 'layMeld', cardIds: staged });
    setStaged([]); setSelected(new Set());
  }
  function guestDoDiscard(cid) {
    if (state.turn !== 'player' || state.phase !== 'play') return;
    mpConn.send({ type: 'doDiscard', cardId: cid });
    setSelected(new Set());
  }

  // ---- Actions ----
  function flashStatus(text) {
    setStatusFlash(text);
    setTimeout(() => setStatusFlash(null), 1200);
  }

  function toggleSelect(id) {
    const next = new Set(selected);
    if (next.has(id)) next.delete(id); else next.add(id);
    setSelected(next);
    sfx('select');
  }

  function toggleStage(id) {
    if (state.turn !== 'player' || state.phase !== 'play') return;
    if (staged.includes(id)) setStaged(staged.filter(x => x !== id));
    else setStaged([...staged, id]);
    sfx('stage');
  }

  // Player: draw deck
  function drawDeck() {
    if (mpMode === 'guest') { guestDrawDeck(); return; }
    if (state.turn !== 'player' || state.phase !== 'draw') return;
    if (!state.deck.length) {
      setState(s => {
        const top = s.discard[s.discard.length - 1];
        const rest = s.discard.slice(0, -1);
        return { ...s, deck: window.RomeEngine.shuffle(rest), discard: [top], phase: 'play' };
      });
      return;
    }
    sfx('draw');
    const c = state.deck[0];
    setDrawnAnim({ card: c, from: 'deck' });
    setDrawnCardId(c.id);
    setTimeout(() => setDrawnAnim(null), 800);
    setState(s => {
      const [card, ...rest] = s.deck;
      return { ...s, deck: rest, player: { ...s.player, hand: [...s.player.hand, card] }, phase: 'play' };
    });
  }

  function drawDiscard() {
    if (mpMode === 'guest') { guestDrawDiscard(); return; }
    if (state.turn !== 'player' || state.phase !== 'draw') return;
    if (!state.discard.length) return;
    sfx('drawDiscard');
    const c = state.discard[state.discard.length - 1];
    setDrawnAnim({ card: c, from: 'discard' });
    setDrawnCardId(c.id);
    setTimeout(() => setDrawnAnim(null), 800);
    setState(s => {
      const card = s.discard[s.discard.length - 1];
      return { ...s, discard: s.discard.slice(0, -1), player: { ...s.player, hand: [...s.player.hand, card] }, phase: 'play' };
    });
  }

  function layMeld() {
    if (mpMode === 'guest') { guestLayMeld(); return; }
    if (state.turn !== 'player' || state.phase !== 'play') return;
    if (!stagedMeldStatus.ok) {
      flashStatus('Invalid meld');
      sfx('bad');
      return;
    }
    sfx('meld');
    const pts = window.RomeEngine.meldPoints(stagedCards);
    // Check classic open-40 rule: can't open if pending + pts still < 40 AND this is not *completing* the open
    setState(s => {
      const needOpen = settings.requireOpen40 && !s.player.hasOpenedMeld;
      const newOpeningPts = s.player.openingPoints + pts;
      const opensNow = needOpen && newOpeningPts >= 40;
      if (needOpen && !opensNow) {
        // Still allow staging toward 40 — accumulate
        const newMeld = { id: window.meldId(), type: window.RomeEngine.isValidSet(stagedCards) ? 'set' : 'run', cards: stagedCards, owner: 'you' };
        const newHand = s.player.hand.filter(c => !staged.includes(c.id));
        pushToast({ kind: 'system', text: `Opening progress: ${newOpeningPts}/40 — lay more to unlock` });
        return {
          ...s,
          player: { ...s.player, hand: newHand, melds: [...s.player.melds, newMeld], openingPoints: newOpeningPts },
          melds: [...s.melds, newMeld]
        };
      }
      const newMeld = { id: window.meldId(), type: window.RomeEngine.isValidSet(stagedCards) ? 'set' : 'run', cards: stagedCards, owner: 'you' };
      const newHand = s.player.hand.filter(c => !staged.includes(c.id));
      return {
        ...s,
        player: { ...s.player, hand: newHand, melds: [...s.player.melds, newMeld], hasOpenedMeld: true, openingPoints: newOpeningPts },
        melds: [...s.melds, newMeld]
      };
    });
    flashStatus(`+${pts}`);
    setStaged([]);
    setSelected(new Set());
  }

  function layOffCards(targetMeld, cards, ids) {
    if (!canPlay) return;
    if (!settings.layoffOpponent) { flashStatus('Layoff disabled'); return; }
    if (settings.requireOpen40 && !state.player.hasOpenedMeld) { flashStatus('Open with 40pts first'); return; }
    if (!window.RomeEngine.isValidMeld([...targetMeld.cards, ...cards])) { flashStatus('Invalid layoff'); sfx('bad'); return; }
    sfx('meld');
    flashStatus(`+${window.RomeEngine.meldPoints([...targetMeld.cards, ...cards]) - window.RomeEngine.meldPoints(targetMeld.cards)}`);
    const idSet = new Set(ids);
    setState(s => {
      const newHand = s.player.hand.filter(c => !idSet.has(c.id));
      const won = newHand.length === 0;
      return {
        ...s,
        player: { ...s.player, hand: newHand, hasOpenedMeld: true },
        melds: s.melds.map(m => m.id === targetMeld.id ? { ...m, cards: [...m.cards, ...cards] } : m),
        phase: won ? 'done' : s.phase,
        winner: won ? 'player' : null
      };
    });
    setStaged([]);
    setSelected(new Set());
  }

  function layOff(targetMeld) {
    layOffCards(targetMeld, stagedCards, staged);
  }

  function laySelectedMeld() {
    if (!canPlay || !selectedMeldStatus) return;
    const cards = [...selected].map(id => handMap.get(id)).filter(Boolean);
    const pts = window.RomeEngine.meldPoints(cards);
    sfx('meld');
    setState(s => {
      const needOpen = settings.requireOpen40 && !s.player.hasOpenedMeld;
      const newOpeningPts = s.player.openingPoints + pts;
      const opensNow = needOpen && newOpeningPts >= 40;
      const newMeld = { id: window.meldId(), type: window.RomeEngine.isValidSet(cards) ? 'set' : 'run', cards, owner: 'you' };
      const selIds = new Set([...selected]);
      const newHand = s.player.hand.filter(c => !selIds.has(c.id));
      if (needOpen && !opensNow) {
        pushToast({ kind: 'system', text: `Opening progress: ${newOpeningPts}/40 — lay more to unlock` });
        return { ...s, player: { ...s.player, hand: newHand, melds: [...s.player.melds, newMeld], openingPoints: newOpeningPts }, melds: [...s.melds, newMeld] };
      }
      return { ...s, player: { ...s.player, hand: newHand, melds: [...s.player.melds, newMeld], hasOpenedMeld: true, openingPoints: newOpeningPts }, melds: [...s.melds, newMeld] };
    });
    flashStatus(`+${pts}`);
    setSelected(new Set());
  }

  function discardSelected() {
    if (state.turn !== 'player' || state.phase !== 'play') return;
    if (selected.size !== 1) { flashStatus('Pick exactly 1 to discard'); return; }
    const cid = [...selected][0];
    if (staged.includes(cid)) { flashStatus('Unstage first'); return; }
    doDiscard(cid);
  }

  function doDiscard(cid) {
    if (mpMode === 'guest') { guestDoDiscard(cid); return; }
    if (state.turn !== 'player' || state.phase !== 'play') return;
    if (staged.includes(cid)) { flashStatus('Unstage first'); return; }
    sfx('discard');
    setState(s => {
      const card = s.player.hand.find(c => c.id === cid);
      if (!card) return s;
      const newHand = s.player.hand.filter(c => c.id !== cid);
      const won = newHand.length === 0;
      return {
        ...s,
        player: { ...s.player, hand: newHand },
        discard: [...s.discard, card],
        turn: won ? s.turn : 'ai',
        phase: won ? 'done' : 'draw',
        winner: won ? 'player' : null
      };
    });
    setSelected(new Set());
  }

  // Detect victory
  uE(() => {
    if (state.winner && !victory) {
      setVictory(state.winner);
      sfx(state.winner === 'player' ? 'win' : 'lose');
      if (settings.trashTalk) {
        const key = state.winner === 'player' ? 'lose' : 'win';
        setTimeout(() => pushToast({ kind: 'ai', who: config.personality.toUpperCase(), text: window.RomeAI.randLine(config.personality, key) }), 600);
      }
    }
  }, [state.winner, victory, settings.trashTalk, config.personality]);

  // --- MULTIPLAYER EFFECTS ---
  uE(() => {
    if (mpMode !== 'host' || !mpConn) return;
    let alive = true;

    function hostHandleGuestDraw(fromDeck) {
      setState(s => {
        if (s.turn !== 'ai' || s.phase !== 'draw') return s;
        if (fromDeck) {
          if (!s.deck.length) {
            const top = s.discard[s.discard.length - 1];
            const rest = s.discard.slice(0, -1);
            return { ...s, deck: window.RomeEngine.shuffle(rest), discard: [top], phase: 'play' };
          }
          const [c, ...rest] = s.deck;
          return { ...s, deck: rest, ai: { ...s.ai, hand: [...s.ai.hand, c] }, phase: 'play' };
        } else {
          if (!s.discard.length) return s;
          const c = s.discard[s.discard.length - 1];
          return { ...s, discard: s.discard.slice(0, -1), ai: { ...s.ai, hand: [...s.ai.hand, c] }, phase: 'play' };
        }
      });
    }

    function hostHandleGuestLayMeld(cardIds) {
      setState(s => {
        if (s.turn !== 'ai' || s.phase !== 'play') return s;
        const cards = s.ai.hand.filter(c => cardIds.includes(c.id));
        if (cards.length < 3 || !window.RomeEngine.isValidMeld(cards)) return s;
        const newMeld = { id: window.meldId(), type: window.RomeEngine.isValidSet(cards) ? 'set' : 'run', cards, owner: 'ai' };
        const newHand = s.ai.hand.filter(c => !cardIds.includes(c.id));
        return {
          ...s,
          ai: { ...s.ai, hand: newHand, melds: [...s.ai.melds, newMeld], hasOpenedMeld: true },
          melds: [...s.melds, newMeld]
        };
      });
    }

    function hostHandleGuestDiscard(cardId) {
      setState(s => {
        if (s.turn !== 'ai' || s.phase !== 'play') return s;
        const card = s.ai.hand.find(c => c.id === cardId);
        if (!card) return s;
        const newHand = s.ai.hand.filter(c => c.id !== cardId);
        const won = newHand.length === 0;
        return {
          ...s,
          ai: { ...s.ai, hand: newHand },
          discard: [...s.discard, card],
          turn: won ? s.turn : 'player',
          phase: won ? 'done' : 'draw',
          winner: won ? 'ai' : null
        };
      });
    }

    mpConn.on('data', msg => {
      if (!alive) return;
      if (msg.type === 'drawDeck') hostHandleGuestDraw(true);
      else if (msg.type === 'drawDiscard') hostHandleGuestDraw(false);
      else if (msg.type === 'layMeld') hostHandleGuestLayMeld(msg.cardIds);
      else if (msg.type === 'doDiscard') hostHandleGuestDiscard(msg.cardId);
    });
    mpConn.on('close', () => { if (alive) setMpDisconnected(true); });
    mpConn.on('error', () => { if (alive) setMpDisconnected(true); });
    return () => { alive = false; };
  }, [mpMode, mpConn]);

  uE(() => {
    if (mpMode !== 'host' || !mpConn) return;
    try { mpConn.send({ type: 'state', state: buildGuestView(state) }); } catch (e) {}
  }, [state]);

  uE(() => {
    if (mpMode !== 'guest' || !mpConn) return;
    let alive = true;
    mpConn.on('data', msg => {
      if (!alive) return;
      if (msg.type === 'state') { setState(msg.state); setMpSynced(true); }
    });
    mpConn.on('close', () => { if (alive) setMpDisconnected(true); });
    mpConn.on('error', () => { if (alive) setMpDisconnected(true); });
    return () => { alive = false; };
  }, [mpMode, mpConn]);

  // --- AI TURN ---
  uE(() => {
    if (mpMode) return;
    if (state.turn !== 'ai' || state.winner || state.phase !== 'draw') return;
    runAiTurn();
    // eslint-disable-next-line
  }, [state.turn, state.phase]);

  async function runAiTurn() {
    setAiThinking(true);
    sfx('aiTurn');
    const wait = (ms) => new Promise(r => setTimeout(r, ms));
    await wait(700 + Math.random() * 600);

    // 1) decide draw
    const s0 = stateRef.current;
    const top = s0.discard[s0.discard.length - 1];
    const take = window.RomeAI.shouldTakeDiscard(top, s0.ai.hand, settings.difficulty, s0.ai.hasOpenedMeld);

    if (take && top) {
      setPulsingPile('discard');
      if (settings.trashTalk) pushToast({ kind: 'ai', who: config.personality.toUpperCase(), text: window.RomeAI.randLine(config.personality, 'drawDiscard') });
      setState(s => ({
        ...s,
        discard: s.discard.slice(0, -1),
        ai: { ...s.ai, hand: [...s.ai.hand, top], picked: [...(s.ai.picked||[]), top] },
        phase: 'play'
      }));
    } else {
      setPulsingPile('deck');
      if (Math.random() < 0.5 && settings.trashTalk) pushToast({ kind: 'ai', who: config.personality.toUpperCase(), text: window.RomeAI.randLine(config.personality, 'drawDeck') });
      setState(s => {
        if (!s.deck.length) {
          const t = s.discard[s.discard.length - 1];
          const rest = s.discard.slice(0, -1);
          return { ...s, deck: window.RomeEngine.shuffle(rest), discard: [t], phase: 'play' };
        }
        const [c, ...rest] = s.deck;
        return { ...s, deck: rest, ai: { ...s.ai, hand: [...s.ai.hand, c] }, phase: 'play' };
      });
    }
    await wait(600);
    setPulsingPile(null);
    await wait(400 + Math.random() * 400);

    // 2) think + meld
    const s1 = stateRef.current;
    let hand = s1.ai.hand.slice();
    let newMelds = [];
    let openingPts = s1.ai.openingPoints;
    let hasOpened = s1.ai.hasOpenedMeld;

    const picks = window.RomeAI.pickBestMeldSet(hand);
    // classic: require cumulative 40+ before laying anything
    const needOpen = settings.requireOpen40 && !hasOpened;
    let canOpen = true;
    if (needOpen) {
      const total = picks.reduce((t, m) => t + window.RomeEngine.meldPoints(m.cards), 0);
      canOpen = total >= 40;
    }
    if (picks.length && (!needOpen || canOpen)) {
      for (const m of picks) {
        const pts = window.RomeEngine.meldPoints(m.cards);
        openingPts += pts;
        hand = hand.filter(h => !m.cards.some(c => c.id === h.id));
        newMelds.push({ id: window.meldId(), type: m.type, cards: m.cards, owner: 'ai' });
      }
      hasOpened = true;
      if (settings.trashTalk) pushToast({ kind: 'ai', who: config.personality.toUpperCase(), text: window.RomeAI.randLine(config.personality, 'meld') });
      sfx('meld');
    } else if (settings.trashTalk && Math.random() < 0.4) {
      pushToast({ kind: 'system', text: `${window.RomeAI.PERSONALITIES[config.personality].name} grumbles inaudibly…` });
    }

    // If AI emptied hand with meld, they need to still discard one — but if they have zero cards after melding, they win (Romé-style)
    if (hand.length === 0 && newMelds.length > 0) {
      setState(s => ({
        ...s,
        ai: { ...s.ai, hand: [], melds: [...s.ai.melds, ...newMelds], hasOpenedMeld: true, openingPoints: openingPts },
        melds: [...s.melds, ...newMelds],
        winner: 'ai',
        phase: 'done'
      }));
      setAiThinking(false);
      return;
    }

    await wait(500);

    // 3) discard
    const top2 = s1.discard[s1.discard.length - 1];
    const discardCard = window.RomeAI.pickDiscard(hand, settings.difficulty, s1.discard, []);
    hand = hand.filter(c => c.id !== discardCard.id);
    sfx('discard');
    if (settings.trashTalk && Math.random() < 0.6) pushToast({ kind: 'ai', who: config.personality.toUpperCase(), text: window.RomeAI.randLine(config.personality, 'discard') });

    setState(s => {
      const won = hand.length === 0;
      return {
        ...s,
        ai: {
          ...s.ai,
          hand,
          melds: [...s.ai.melds, ...newMelds],
          hasOpenedMeld: hasOpened,
          openingPoints: openingPts
        },
        melds: [...s.melds, ...newMelds],
        discard: [...s.discard, discardCard],
        turn: won ? s.turn : 'player',
        phase: won ? 'done' : 'draw',
        winner: won ? 'ai' : null
      };
    });
    setAiThinking(false);
  }

  function restart() {
    setState(window.makeInitialGameState(config));
    setSelected(new Set()); setStaged([]); setVictory(null);
    if (settings.trashTalk) pushToast({ kind: 'ai', who: config.personality.toUpperCase(), text: window.RomeAI.randLine(config.personality, 'greet') });
  }

  function switchAI(newKey) {
    // apply tweak live
    const newCfg = { ...config, personality: newKey };
    setState(s => ({ ...s, config: newCfg }));
  }

  const yourTurn = state.turn === 'player' && !state.winner;
  const canDraw = yourTurn && state.phase === 'draw';
  const canPlay = yourTurn && state.phase === 'play';

  return (
    <div className="stage">
      <div className="grid-floor" />
      <div className="scanline" />

      {/* Header */}
      <div className="topbar">
        <div className="brand">
          <div className="logo">Rommé</div>
          <div className="logo-sub">· {config.variant === 'classic' ? 'Classic·40' : 'Simple'} · {settings.difficulty}</div>
        </div>
        <div className="topbar-right">
          <div className="chip" onClick={() => setShowHelp(true)}>?</div>
          <div className="chip" onClick={() => setShowTweaks(v => !v)}>⚙ Tweaks</div>
          <div className="chip" onClick={onQuit}>←</div>
        </div>
      </div>

      {/* Opponent area */}
      <div className="opponent-area">
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4 }}>
          {mpMode ? (
            <div className="chip" style={{ fontFamily: '"JetBrains Mono"', fontSize: 11, letterSpacing: '.15em', padding: '6px 14px' }}>
              {mpMode === 'host' ? '● PLAYER 2' : '● PLAYER 1'}{state.turn === 'ai' && !state.winner ? ' ···' : ''}
            </div>
          ) : (
            <OpponentBadge
              personality={config.personality}
              active={state.turn === 'ai' && !state.winner}
              handCount={state.ai.hand.length}
              thinking={aiThinking}
            />
          )}
          <div className="opponent-hand">
            {state.ai.hand.slice(0, Math.min(14, state.ai.hand.length)).map((c, i, arr) => (
              <CardBack key={c.id} design={settings.cardBack}
                rotation={(i - (arr.length - 1) / 2) * 3}
                style={{ marginLeft: i === 0 ? 0 : -22 }} />
            ))}
          </div>
        </div>
      </div>

      {/* Center: melds + piles */}
      <div className="table-center">
        <div className={`melds-area ${state.melds.length === 0 ? 'empty' : ''}`}>
          {state.melds.map(m => {
            const layoffBase = canPlay && settings.layoffOpponent &&
              !(settings.requireOpen40 && !state.player.hasOpenedMeld);
            const isTarget = layoffBase && layoffCandidates.length > 0 &&
              window.RomeEngine.isValidMeld([...m.cards, ...layoffCandidates]);
            const isLayoffMode = layoffBase && layoffCandidates.length > 0;
            return (
              <div key={m.id}
                className={isTarget ? 'layoff-target' : isLayoffMode ? 'layoff-blocked' : ''}
                onClick={() => {
                  if (isTarget) {
                    layOffCards(m, layoffCandidates, layoffCandidates.map(c => c.id));
                    return;
                  }
                  if (!isLayoffMode) return;
                  if (m.type === 'set') {
                    if (m.cards.length >= 4) { flashStatus('Set is full (4 max)'); }
                    else {
                      const meldSuits = new Set(m.cards.filter(c => !c.joker).map(c => c.suit));
                      const dupSuit = layoffCandidates.some(sc => !sc.joker && meldSuits.has(sc.suit));
                      flashStatus(dupSuit ? 'Suit already in this set' : 'Wrong rank for this set');
                    }
                  } else {
                    flashStatus("Doesn't extend this run");
                  }
                  sfx('bad');
                }}>
                <MeldGroup meld={m} />
              </div>
            );
          })}
        </div>

        <div className="center-piles">
          <Pile label="Deck"
            count={state.deck.length}
            className={`${canDraw ? 'clickable your-turn-highlight' : ''} ${pulsingPile === 'deck' ? 'glow-pink' : ''}`}
            onClick={canDraw ? drawDeck : undefined}
          >
            <CardBack design={settings.cardBack} style={{ width: '100%', height: '100%' }} />
          </Pile>
          <Pile label="Discard"
            count={state.discard.length}
            className={`discard-drop ${canDraw ? 'clickable your-turn-highlight' : ''} ${pulsingPile === 'discard' ? 'glow-cyan' : ''} ${dnd.hoverZone === 'discard' ? 'drop-active' : ''}`}
            onClick={canDraw ? drawDiscard : undefined}
          >
            {state.discard.length > 0 ? (
              <Card card={state.discard[state.discard.length - 1]} style={{ width: '100%', height: '100%' }} />
            ) : (
              <div style={{ width: '100%', height: '100%', border: '1px dashed rgba(255,255,255,.15)', borderRadius: 10 }} />
            )}
          </Pile>
        </div>
      </div>

      {/* Staging above hand */}
      <div className="player-area">
        <div className={`staging staging-drop ${staged.length > 0 ? 'active' : ''} ${dnd.hoverZone === 'staging' ? 'drop-active' : ''}`}>
          {staged.length > 0 ? (
            <div className="staging-inner">
              <div className="staging-cards">
                {stagedCards.map(c => (
                  <Card
                    key={c.id}
                    card={c}
                    data-card-id={c.id}
                    onPointerDown={(e) => dnd.startDrag(e, c.id, 'staging')}
                    onClick={() => toggleStage(c.id)}
                  />
                ))}
              </div>
              <div className="staging-controls">
                <span className="staging-label">{canLayoffSomewhere && !stagedMeldStatus.ok ? 'Lay off' : 'Meld'}</span>
                <span className={`staging-status ${stagedMeldStatus.ok || canLayoffSomewhere ? 'ok' : 'bad'}`}>
                  {stagedMeldStatus.ok
                    ? (stagedMeldStatus.warn || `Valid · ${stagedMeldStatus.points}pts`)
                    : canLayoffSomewhere ? 'Tap a meld ↑'
                    : stagedMeldStatus.reason}
                </span>
                <button className="btn primary" disabled={!stagedMeldStatus.ok} onClick={layMeld}>Lay down</button>
                <button className="btn ghost" onClick={() => setStaged([])}>Cancel</button>
              </div>
            </div>
          ) : (
            dnd.dragId && dnd.hoverZone === 'staging' ? (
              <div className="empty-staging-dropzone show">Drop to stage</div>
            ) : null
          )}
        </div>

        {/* Action bar */}
        <div className="action-bar">
          {selectedMeldStatus ? (
            <button className="btn primary" onClick={laySelectedMeld}>
              Lay down · {selectedMeldStatus.warn || `${selectedMeldStatus.points}pts`}
            </button>
          ) : (
            <button className="btn" disabled={!canPlay || selected.size === 0} onClick={() => {
              const ids = [...selected].filter(id => !staged.includes(id));
              setStaged([...staged, ...ids]);
              setSelected(new Set());
            }}>+ Stage ({selected.size})</button>
          )}
          <button className="btn primary" disabled={!canPlay || selected.size !== 1 || staged.includes([...selected][0])} onClick={discardSelected}>
            Discard <span className="kbd">↩</span>
          </button>
          <button className="btn ghost" onClick={() => {
            setState(s => {
              // Cycle: by suit → by rank → current-order-but-grouped
              const cur = s.player.hand;
              const bySuit = window.RomeEngine.sortHand(cur);
              const signature = (arr) => arr.map(c => c.id).join(',');
              if (signature(cur) === signature(bySuit)) {
                // already sorted by suit → sort by rank
                const byRank = cur.slice().sort((a, b) => {
                  if (a.joker && !b.joker) return 1;
                  if (!a.joker && b.joker) return -1;
                  if (a.joker && b.joker) return 0;
                  const order = { '♠':0, '♥':1, '♦':2, '♣':3 };
                  const rv = window.RomeEngine.RANK_VAL;
                  if (rv[a.rank] !== rv[b.rank]) return rv[a.rank] - rv[b.rank];
                  return order[a.suit] - order[b.suit];
                });
                return { ...s, player: { ...s.player, hand: byRank } };
              }
              return { ...s, player: { ...s.player, hand: bySuit } };
            });
            setSelected(new Set());
          }}>↕ Sort</button>
          {selected.size > 0 && (
            <button className="btn ghost" onClick={() => setSelected(new Set())}>
              Clear ({selected.size})
            </button>
          )}
          <button className="btn ghost" onClick={() => {
            // hint: show best meld
            const picks = window.RomeAI.pickBestMeldSet(state.player.hand);
            if (picks.length) {
              const best = picks[0];
              setSelected(new Set(best.cards.map(c => c.id)));
              pushToast({ kind: 'system', text: `Hint: ${best.cards.length}-card ${best.type}` });
            } else {
              pushToast({ kind: 'system', text: 'No melds yet — keep drawing' });
            }
          }}>💡 Hint</button>
        </div>

        {/* Player hand */}
        <div className={`player-hand density-${settings.density}`} ref={handRef}>
          {state.player.hand.map(c => {
            const isStaged = staged.includes(c.id);
            const isSelected = selected.has(c.id);
            const isHovered = dnd.hoverId === c.id && dnd.hoverZone === 'hand';
            return (
              <Card
                key={c.id}
                card={c}
                data-card-id={c.id}
                className={isHovered ? 'drop-before' : ''}
                selected={isSelected || isStaged}
                dim={isStaged}
                onPointerDown={(e) => dnd.startDrag(e, c.id, 'hand')}
                onClick={() => {
                  if (!canPlay && !canDraw) return;
                  if (state.phase === 'draw') { pushToast({ kind: 'system', text: 'Draw a card first' }); return; }
                  toggleSelect(c.id);
                }}
              />
            );
          })}
        </div>
      </div>

      {/* Toasts */}
      <div className="toast-stack">
        {toasts.map(t => <Toast key={t.id} toast={t} />)}
      </div>

      {/* Status flash */}
      {statusFlash && <div className="status-pill">{statusFlash}</div>}

      {/* Drawn card announce */}
      {drawnAnim && <div className="status-pill" style={{top:'38%'}}>+ {drawnAnim.card.joker ? 'JOKER ★' : drawnAnim.card.rank + drawnAnim.card.suit}</div>}

      {/* Tweaks */}
      {showTweaks && (
        <TweaksPanel
          settings={settings}
          onChange={setSettings}
          onClose={() => setShowTweaks(false)}
          onSwitchAI={switchAI}
        />
      )}

      {/* Help */}
      {showHelp && <HelpModal onClose={() => setShowHelp(false)} config={{ variant: config.variant, layoffAllowed: settings.layoffOpponent }} />}

      {/* Victory */}
      {victory && <VictoryModal winner={victory} personality={config.personality} onAgain={mpMode ? onQuit : restart} onMenu={onQuit} />}

      {/* Multiplayer: waiting for first sync */}
      {mpMode === 'guest' && !mpSynced && (
        <div style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,.75)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 80, flexDirection: 'column', gap: 16, fontFamily: '"JetBrains Mono"', letterSpacing: '.15em', color: 'var(--neon-cyan)', fontSize: 13, textTransform: 'uppercase' }}>
          ⏳ Waiting for host…
        </div>
      )}

      {/* Multiplayer: disconnected */}
      {mpDisconnected && (
        <div style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,.82)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 80, flexDirection: 'column', gap: 20 }}>
          <div style={{ fontFamily: '"JetBrains Mono"', fontSize: 13, letterSpacing: '.15em', color: 'var(--danger)', textTransform: 'uppercase' }}>Connection lost</div>
          <button className="cta" onClick={onQuit}>← Back to menu</button>
        </div>
      )}
    </div>
  );
}

window.Game = Game;
