// ══════════════════════════════════════════════════════════════
//  CICLISMO DE MESA ONLINE — Cliente React
//  Réplica digital del juego de mesa de Juegos Garate
//  © 2026 Grupo Yaiza
// ══════════════════════════════════════════════════════════════

const { useState, useEffect, useRef, useCallback, useMemo } = React;

const CELL_COLORS = {
  salida: { bg: '#D32F2F', text: '#FFD700' }, meta: { bg: '#FFD700', text: '#000' },
  peloton: { bg: '#43A047', text: '#fff' }, juez: { bg: '#D32F2F', text: '#fff' },
  director: { bg: '#FB8C00', text: '#000' }, ordenes: { bg: '#E65100', text: '#fff' },
  montana: { bg: '#2E7D32', text: '#fff' }, tiron: { bg: '#1E88E5', text: '#fff' },
  sprint: { bg: '#FDD835', text: '#000' }, caida: { bg: '#E91E63', text: '#fff' },
  espera: { bg: '#212121', text: '#fff' }, goma: { bg: '#8E24AA', text: '#fff' },
  irregularidad: { bg: '#FF6F00', text: '#fff' },
};

const TEAM_COLORS = {
  rojo: { primary: '#E53935', light: '#FF6659', dark: '#AB000D' },
  azul: { primary: '#1E88E5', light: '#6AB7FF', dark: '#005CB2' },
  verde: { primary: '#43A047', light: '#76D275', dark: '#00701A' },
  amarillo: { primary: '#FDD835', light: '#FFFF6B', dark: '#C6A700' },
  naranja: { primary: '#FB8C00', light: '#FFBD45', dark: '#C25E00' },
  morado: { primary: '#8E24AA', light: '#C158DC', dark: '#5C007A' },
};

function buildSerpentineLayout() {
  const layout = [], ROWS = 10;
  for (let r = ROWS - 1; r >= 0; r--) layout.push({ pos: ROWS - 1 - r, col: 0, row: r });
  for (let r = 0; r < ROWS; r++) layout.push({ pos: 10 + r, col: 1, row: r });
  for (let r = ROWS - 1; r >= 0; r--) layout.push({ pos: 20 + (ROWS - 1 - r), col: 2, row: r });
  for (let r = 0; r < ROWS; r++) layout.push({ pos: 30 + r, col: 3, row: r });
  layout.push({ pos: 40, col: 3, row: -1 });
  return layout;
}
const SERPENTINE = buildSerpentineLayout();

function CyclistPiece({ color, dorsal, size = 28, selected, blocked, onClick }) {
  const tc = TEAM_COLORS[color] || TEAM_COLORS.rojo;
  return (
    <div onClick={onClick} style={{
      width: size, height: size * 1.4, borderRadius: `${size/2}px ${size/2}px ${size/3}px ${size/3}px`,
      background: selected ? '#fff' : '#f5f0e8',
      border: `2px solid ${selected ? tc.primary : '#888'}`,
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      cursor: onClick ? 'pointer' : 'default', position: 'relative',
      boxShadow: selected ? `0 0 8px ${tc.primary}` : '0 1px 3px rgba(0,0,0,0.3)',
      transform: selected ? 'scale(1.15)' : 'scale(1)', transition: 'all 0.2s',
      opacity: blocked ? 0.5 : 1,
    }}>
      <div style={{ width: size * 0.5, height: size * 0.35, borderRadius: '50%', background: tc.light || tc.primary, border: `1px solid ${tc.dark}`, marginBottom: 1 }}/>
      <div style={{ width: size * 0.7, height: size * 0.45, borderRadius: '2px', background: tc.primary, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: size * 0.3, fontWeight: 'bold', color: '#fff', textShadow: '0 1px 1px rgba(0,0,0,0.5)' }}>{dorsal}</div>
    </div>
  );
}

function DiceRoller({ value, rolling, onRoll, disabled }) {
  const dots = { 1: [[1,1]], 2: [[0,0],[2,2]], 3: [[0,0],[1,1],[2,2]], 4: [[0,0],[0,2],[2,0],[2,2]], 5: [[0,0],[0,2],[1,1],[2,0],[2,2]], 6: [[0,0],[0,2],[1,0],[1,2],[2,0],[2,2]] };
  const currentDots = dots[value] || dots[1];
  return (
    <div style={{ textAlign: 'center' }}>
      <div onClick={disabled ? null : onRoll} style={{
        width: 72, height: 72, background: '#fff', borderRadius: 12, border: '3px solid #333', display: 'inline-grid',
        gridTemplateColumns: 'repeat(3, 1fr)', gridTemplateRows: 'repeat(3, 1fr)', padding: 8,
        cursor: disabled ? 'not-allowed' : 'pointer',
        boxShadow: rolling ? '0 0 20px rgba(255,215,0,0.8)' : '0 2px 8px rgba(0,0,0,0.3)',
        animation: rolling ? 'diceShake 0.15s infinite' : 'none', opacity: disabled ? 0.5 : 1, transition: 'box-shadow 0.3s',
      }}>
        {[0,1,2].map(r => [0,1,2].map(c => (
          <div key={`${r}${c}`} style={{ width: 14, height: 14, borderRadius: '50%', margin: 'auto', background: currentDots.some(([dr,dc]) => dr === r && dc === c) ? '#333' : 'transparent', transition: 'background 0.2s' }}/>
        )))}
      </div>
      {!disabled && <div style={{ marginTop: 8, fontSize: 13, color: '#aaa' }}>Toca para tirar</div>}
    </div>
  );
}

function BoardCell({ cell, cyclists, cellSize, isHighlighted, onClick }) {
  const colors = CELL_COLORS[cell.tipo] || CELL_COLORS.peloton;
  const displayNum = 40 - cell.pos;
  const shortText = cell.tipo === 'salida' ? 'SALIDA' : cell.tipo === 'meta' ? 'META' : cell.tipo === 'peloton' ? 'PEL' : cell.tipo === 'juez' ? 'JUEZ' : cell.tipo === 'director' ? 'DIR' : cell.tipo === 'montana' ? '⛰️' : cell.tipo === 'tiron' ? '+2' : cell.tipo === 'sprint' ? '🔄' : cell.tipo === 'caida' ? '-2' : cell.tipo === 'espera' ? '⏸' : cell.tipo === 'goma' ? '-3' : cell.tipo === 'irregularidad' ? '-4' : '';
  return (
    <div onClick={onClick} style={{
      width: cellSize, height: cellSize, background: colors.bg, color: colors.text, borderRadius: 4, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', position: 'relative',
      border: isHighlighted ? '3px solid #FFD700' : '1px solid rgba(255,255,255,0.2)',
      boxShadow: isHighlighted ? '0 0 12px rgba(255,215,0,0.6)' : 'inset 0 1px 2px rgba(0,0,0,0.2)',
      fontSize: cellSize * 0.18, fontWeight: 'bold', cursor: 'default', transition: 'all 0.3s',
    }}>
      <div style={{ fontSize: cellSize * 0.22, opacity: 0.8 }}>{displayNum}</div>
      <div style={{ fontSize: cellSize * 0.2 }}>{shortText}</div>
      {cyclists.length > 0 && (
        <div style={{ position: 'absolute', bottom: -2, right: -2, display: 'flex', gap: 1, flexWrap: 'wrap', maxWidth: cellSize + 10 }}>
          {cyclists.slice(0, 6).map((c) => (
            <div key={c.id} style={{ width: 10, height: 10, borderRadius: '50%', background: TEAM_COLORS[c.teamColor]?.primary || '#888', border: '1px solid #fff', fontSize: 6, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{c.dorsalNum}</div>
          ))}
          {cyclists.length > 6 && <div style={{ fontSize: 8, color: '#fff' }}>+{cyclists.length - 6}</div>}
        </div>
      )}
    </div>
  );
}

function GameBoard({ tablero, teams, highlightedCell }) {
  const cellSize = Math.min(65, (window.innerWidth - 40) / 5);
  const cyclistsByPos = useMemo(() => {
    const map = {};
    if (!teams) return map;
    teams.forEach(team => { team.ciclistas.forEach((c, idx) => { if (!map[c.pos]) map[c.pos] = []; map[c.pos].push({ ...c, teamColor: team.equipoId, dorsalNum: c.dorsal || (idx + 1), teamName: team.nombre }); }); });
    return map;
  }, [teams]);
  return (
    <div style={{ display: 'grid', gridTemplateColumns: `repeat(4, ${cellSize + 4}px)`, gridTemplateRows: `repeat(11, ${cellSize + 4}px)`, gap: 2, padding: 8, background: '#2d2d2d', borderRadius: 12, boxShadow: '0 4px 20px rgba(0,0,0,0.5)', margin: '0 auto', width: 'fit-content' }}>
      {tablero && SERPENTINE.map(({ pos, col, row }) => { const cell = tablero[pos]; if (!cell) return null; return (<div key={pos} style={{ gridColumn: col + 1, gridRow: row + 2 }}><BoardCell cell={cell} cyclists={cyclistsByPos[pos] || []} cellSize={cellSize} isHighlighted={highlightedCell === pos} /></div>); })}
      {tablero && tablero[40] && (
        <div style={{ gridColumn: '1 / 5', gridRow: 1 }}>
          <div style={{ background: '#FFD700', color: '#000', borderRadius: 8, padding: 8, textAlign: 'center', fontWeight: 'bold', fontSize: 18, boxShadow: '0 0 15px rgba(255,215,0,0.5)' }}>
            🏆 META — PÓDIUM 🏆
            {(cyclistsByPos[40] || []).length > 0 && (<div style={{ fontSize: 12, marginTop: 4 }}>{(cyclistsByPos[40] || []).map(c => (<span key={c.id} style={{ background: TEAM_COLORS[c.teamColor]?.primary, color: '#fff', padding: '2px 6px', borderRadius: 10, margin: '0 2px', fontSize: 10 }}>#{c.dorsalNum}</span>))}</div>)}
          </div>
        </div>
      )}
    </div>
  );
}

function CyclistSelector({ team, movibles, forzarCiclista, onSelect, selectedId }) {
  if (!team) return null;
  return (
    <div style={{ background: 'rgba(0,0,0,0.3)', borderRadius: 12, padding: 12, border: `2px solid ${TEAM_COLORS[team.equipoId]?.primary || '#888'}` }}>
      <div style={{ color: '#fff', fontSize: 13, marginBottom: 8, textAlign: 'center', fontWeight: 'bold' }}>Elige corredor para avanzar</div>
      <div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap' }}>
        {team.ciclistas.map((c, idx) => {
          const isMovible = movibles.some(m => m.id === c.id);
          const isForzado = forzarCiclista && forzarCiclista === c.id;
          const canClick = isForzado || (!forzarCiclista && isMovible);
          return (
            <div key={c.id} style={{ textAlign: 'center', opacity: canClick ? 1 : 0.35 }}>
              <CyclistPiece color={team.equipoId} dorsal={c.dorsal || (idx + 1)} size={32} selected={selectedId === c.id} blocked={c.bloqueado} onClick={canClick ? () => onSelect(c.id) : null} />
              <div style={{ fontSize: 9, color: c.bloqueado ? '#f44' : '#aaa', marginTop: 2 }}>Cas.{40 - c.pos}{c.bloqueado && ' ⏸'}{c.pierdeTurno && ' ⏭'}</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function CardPopup({ carta, onClose }) {
  if (!carta) return null;
  const isJuez = carta.tipo === 'juez';
  return (
    <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.7)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 }} onClick={onClose}>
      <div style={{ width: 280, minHeight: 180, borderRadius: 16, background: isJuez ? '#D32F2F' : '#FB8C00', color: isJuez ? '#fff' : '#000', padding: 24, textAlign: 'center', boxShadow: '0 8px 40px rgba(0,0,0,0.5)', transform: 'rotate(-2deg)' }}>
        <div style={{ fontSize: 14, opacity: 0.8, marginBottom: 12, fontWeight: 'bold' }}>{isJuez ? '🟥 JUEZ ÁRBITRO' : '🟧 ORDEN DIRECTOR'}</div>
        <div style={{ fontSize: 18, fontWeight: 'bold', lineHeight: 1.4 }}>{carta.texto}</div>
      </div>
    </div>
  );
}

function MontanaSelector({ rivales, onSelect }) {
  if (!rivales?.length) return null;
  return (
    <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.8)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 }}>
      <div style={{ background: '#1a1a1a', borderRadius: 16, padding: 24, maxWidth: 360, border: '2px solid #2E7D32' }}>
        <div style={{ color: '#fff', fontSize: 16, fontWeight: 'bold', textAlign: 'center', marginBottom: 16 }}>⛰️ MONTAÑA — Elige rival para retroceder</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8, maxHeight: 300, overflowY: 'auto' }}>
          {rivales.map(r => (
            <div key={`${r.equipoId}_${r.ciclistaId}`} onClick={() => onSelect(r.equipoId, r.ciclistaId)} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 10, background: 'rgba(255,255,255,0.05)', borderRadius: 8, border: `1px solid ${TEAM_COLORS[r.equipoId]?.primary || '#888'}`, cursor: 'pointer' }}>
              <div style={{ width: 28, height: 28, borderRadius: '50%', background: TEAM_COLORS[r.equipoId]?.primary || '#888', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, color: '#fff', fontWeight: 'bold' }}>{r.dorsal || '?'}</div>
              <div style={{ color: '#fff', flex: 1 }}>
                <div style={{ fontSize: 13, fontWeight: 'bold' }}>{r.equipoNombre} — Dorsal {r.dorsal || '?'}</div>
                <div style={{ fontSize: 11, opacity: 0.7 }}>Casilla {40 - r.posActual} → Pelotón {40 - r.posDestino} (retrocede {r.posActual - r.posDestino})</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function EventLog({ eventos }) {
  const logRef = useRef(null);
  useEffect(() => { if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight; }, [eventos]);
  return (
    <div ref={logRef} style={{ background: 'rgba(0,0,0,0.4)', borderRadius: 8, padding: 8, maxHeight: 150, overflowY: 'auto', fontSize: 12, color: '#ccc' }}>
      {eventos.map((e, i) => <div key={i} style={{ padding: '2px 0', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>{e}</div>)}
      {eventos.length === 0 && <div style={{ opacity: 0.5 }}>Esperando primera jugada...</div>}
    </div>
  );
}

function DirectorChoiceSelector({ rivales, onSelect }) {
  if (!rivales?.length) return null;
  return (
    <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, background: 'rgba(0,0,0,0.8)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000 }}>
      <div style={{ background: '#1a1a1a', borderRadius: 16, padding: 24, maxWidth: 360, border: '2px solid #FB8C00' }}>
        <div style={{ color: '#fff', fontSize: 16, fontWeight: 'bold', textAlign: 'center', marginBottom: 16 }}>🎯 ORDEN DIRECTOR — Elige equipo rival</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {rivales.map(r => (
            <div key={r.equipoId} onClick={() => onSelect(r.equipoId, r.ciclistaId)} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 12, background: 'rgba(255,255,255,0.05)', borderRadius: 8, border: `1px solid ${TEAM_COLORS[r.equipoId]?.primary || '#888'}`, cursor: 'pointer' }}>
              <div style={{ width: 28, height: 28, borderRadius: '50%', background: TEAM_COLORS[r.equipoId]?.primary || '#888', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, color: '#fff', fontWeight: 'bold' }}>{r.dorsal}</div>
              <div style={{ color: '#fff', flex: 1 }}><div style={{ fontSize: 14, fontWeight: 'bold' }}>{r.equipoNombre}</div><div style={{ fontSize: 11, opacity: 0.6 }}>Dorsal {r.dorsal} — Casilla {40 - r.posActual}</div></div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function LanguageSelector({ selectedLang, onSelect, compact }) {
  const [langs, setLangs] = useState([]);
  const [search, setSearch] = useState('');
  const [open, setOpen] = useState(false);
  useEffect(() => { fetch('/api/languages').then(r => r.json()).then(d => { if (d.ok) setLangs(d.languages); }).catch(() => setLangs([{ code: 'es', nombre: 'Español', bandera: '🇪🇸' }, { code: 'en', nombre: 'English', bandera: '🇬🇧' }])); }, []);
  const current = langs.find(l => l.code === selectedLang) || { bandera: '🌐', nombre: selectedLang };
  const filtered = langs.filter(l => l.nombre.toLowerCase().includes(search.toLowerCase()) || l.code.includes(search.toLowerCase()));
  if (compact) {
    return (<div style={{ position: 'relative' }}><button onClick={() => setOpen(!open)} style={{ padding: '4px 10px', borderRadius: 6, border: '1px solid rgba(255,255,255,0.2)', background: 'rgba(255,255,255,0.05)', color: '#fff', fontSize: 12, cursor: 'pointer' }}>{current.bandera} {current.nombre}</button>
      {open && (<div style={{ position: 'absolute', bottom: '100%', left: 0, background: '#1a1a1a', border: '1px solid #444', borderRadius: 8, maxHeight: 200, overflowY: 'auto', width: 200, zIndex: 999, padding: 4 }}>
        <input type="text" value={search} onChange={e => setSearch(e.target.value)} placeholder="Buscar..." style={{ ...inputStyle, padding: '6px 8px', fontSize: 11, marginTop: 0 }} />
        {filtered.map(l => (<div key={l.code} onClick={() => { onSelect(l.code); setOpen(false); setSearch(''); }} style={{ padding: '4px 8px', cursor: 'pointer', fontSize: 11, color: '#fff', borderRadius: 4, background: l.code === selectedLang ? 'rgba(76,175,80,0.2)' : 'transparent' }}>{l.bandera} {l.nombre}</div>))}
      </div>)}</div>);
  }
  return (<div><label style={{ fontSize: 12, opacity: 0.6 }}>Idioma para videochat:</label>
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))', gap: 4, maxHeight: 150, overflowY: 'auto', marginTop: 4, padding: 4, background: 'rgba(0,0,0,0.2)', borderRadius: 8 }}>
      {langs.slice(0, 30).map(l => (<div key={l.code} onClick={() => onSelect(l.code)} style={{ padding: '4px 6px', borderRadius: 4, cursor: 'pointer', fontSize: 10, background: l.code === selectedLang ? 'rgba(76,175,80,0.3)' : 'rgba(255,255,255,0.03)', border: l.code === selectedLang ? '1px solid #4CAF50' : '1px solid transparent', color: '#fff', textAlign: 'center', whiteSpace: 'nowrap', overflow: 'hidden' }}>{l.bandera} {l.nombre}</div>))}
    </div></div>);
}

function VideoChatPanel({ socket, active }) {
  const [localStream, setLocalStream] = useState(null);
  const [peers, setPeers] = useState({});
  const [muted, setMuted] = useState(false);
  const [camOff, setCamOff] = useState(false);
  const [subtitles, setSubtitles] = useState([]);
  const [listening, setListening] = useState(false);
  const localRef = useRef(null);
  const peerConnections = useRef({});
  const recognitionRef = useRef(null);
  const ICE_SERVERS = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };
  useEffect(() => {
    if (!active || !socket) return;
    let stream;
    const startMedia = async () => { try { stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); setLocalStream(stream); if (localRef.current) localRef.current.srcObject = stream; socket.emit('webrtc:join', {}, (res) => { if (res.ok && res.peers) res.peers.forEach(peerId => createPeer(peerId, true, stream)); }); } catch (err) { console.log('Cámara no disponible:', err.message); } };
    const createPeer = (peerId, initiator, mediaStream) => { const pc = new RTCPeerConnection(ICE_SERVERS); peerConnections.current[peerId] = pc; mediaStream?.getTracks().forEach(track => pc.addTrack(track, mediaStream)); pc.onicecandidate = (e) => { if (e.candidate) socket.emit('webrtc:ice', { to: peerId, candidate: e.candidate }); }; pc.ontrack = (e) => { setPeers(prev => ({ ...prev, [peerId]: e.streams[0] })); }; if (initiator) { pc.createOffer().then(offer => { pc.setLocalDescription(offer); socket.emit('webrtc:offer', { to: peerId, offer }); }); } return pc; };
    socket.on('webrtc:newPeer', ({ peerId }) => { if (stream) createPeer(peerId, true, stream); });
    socket.on('webrtc:offer', async ({ from, offer }) => { const pc = createPeer(from, false, stream); await pc.setRemoteDescription(offer); const answer = await pc.createAnswer(); await pc.setLocalDescription(answer); socket.emit('webrtc:answer', { to: from, answer }); });
    socket.on('webrtc:answer', async ({ from, answer }) => { const pc = peerConnections.current[from]; if (pc) await pc.setRemoteDescription(answer); });
    socket.on('webrtc:ice', async ({ from, candidate }) => { const pc = peerConnections.current[from]; if (pc) await pc.addIceCandidate(candidate); });
    socket.on('webrtc:peerLeft', ({ peerId }) => { if (peerConnections.current[peerId]) { peerConnections.current[peerId].close(); delete peerConnections.current[peerId]; } setPeers(prev => { const n = { ...prev }; delete n[peerId]; return n; }); });
    socket.on('voice:translated', ({ text, from, to }) => { setSubtitles(prev => [...prev, { text, from, to, time: Date.now() }].slice(-5)); if ('speechSynthesis' in window) { const utter = new SpeechSynthesisUtterance(text); utter.lang = to; utter.rate = 1; utter.volume = 0.8; speechSynthesis.speak(utter); } });
    startMedia();
    return () => { stream?.getTracks().forEach(t => t.stop()); Object.values(peerConnections.current).forEach(pc => pc.close()); peerConnections.current = {}; socket.emit('webrtc:leave'); socket.off('webrtc:newPeer'); socket.off('webrtc:offer'); socket.off('webrtc:answer'); socket.off('webrtc:ice'); socket.off('webrtc:peerLeft'); socket.off('voice:translated'); if (recognitionRef.current) recognitionRef.current.stop(); };
  }, [active, socket]);
  const toggleListening = () => { if (listening && recognitionRef.current) { recognitionRef.current.stop(); setListening(false); return; } if (!('webkitSpeechRecognition' in window || 'SpeechRecognition' in window)) { alert('Tu navegador no soporta reconocimiento de voz'); return; } const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; const recognition = new SpeechRecognition(); recognition.continuous = true; recognition.interimResults = false; recognition.onresult = (event) => { for (let i = event.resultIndex; i < event.results.length; i++) { if (event.results[i].isFinal) { const transcript = event.results[i][0].transcript.trim(); if (transcript.length > 1) { socket.emit('voice:translate', { text: transcript }); setSubtitles(prev => [...prev, { text: transcript, from: 'yo', to: 'yo', time: Date.now() }].slice(-5)); } } } }; recognition.onerror = () => setListening(false); recognition.onend = () => { if (listening) recognition.start(); }; recognition.start(); recognitionRef.current = recognition; setListening(true); };
  const toggleMute = () => { localStream?.getAudioTracks().forEach(t => { t.enabled = !t.enabled; }); setMuted(!muted); };
  const toggleCam = () => { localStream?.getVideoTracks().forEach(t => { t.enabled = !t.enabled; }); setCamOff(!camOff); };
  if (!active) return null;
  return (
    <div style={{ background: 'rgba(0,0,0,0.4)', borderRadius: 10, padding: 8, marginBottom: 8 }}>
      <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', justifyContent: 'center' }}>
        <div style={{ position: 'relative', width: 80, height: 60, borderRadius: 6, overflow: 'hidden', background: '#000' }}><video ref={localRef} autoPlay muted playsInline style={{ width: '100%', height: '100%', objectFit: 'cover', transform: 'scaleX(-1)' }}/><div style={{ position: 'absolute', bottom: 1, left: 2, fontSize: 8, color: '#fff', background: 'rgba(0,0,0,0.5)', padding: '0 3px', borderRadius: 3 }}>Tú</div></div>
        {Object.keys(peers).map(id => <PeerVideo key={id} stream={peers[id]} />)}
      </div>
      <div style={{ display: 'flex', gap: 6, justifyContent: 'center', marginTop: 6 }}>
        <button onClick={toggleMute} style={{ padding: '4px 10px', borderRadius: 6, border: 'none', background: muted ? '#f44' : '#4CAF50', color: '#fff', fontSize: 11, cursor: 'pointer' }}>{muted ? '🔇' : '🎤'}</button>
        <button onClick={toggleCam} style={{ padding: '4px 10px', borderRadius: 6, border: 'none', background: camOff ? '#f44' : '#4CAF50', color: '#fff', fontSize: 11, cursor: 'pointer' }}>{camOff ? '📷❌' : '📷'}</button>
        <button onClick={toggleListening} style={{ padding: '4px 10px', borderRadius: 6, border: 'none', background: listening ? '#f44' : '#1E88E5', color: '#fff', fontSize: 11, cursor: 'pointer' }}>{listening ? '⏹ Parar traducción' : '🌐 Traducir voz'}</button>
      </div>
      {subtitles.length > 0 && (<div style={{ marginTop: 6, maxHeight: 60, overflowY: 'auto' }}>{subtitles.map((s, i) => (<div key={i} style={{ fontSize: 10, color: s.from === 'yo' ? '#4CAF50' : '#FFD700', padding: '1px 0' }}>{s.from === 'yo' ? '🗣 Tú: ' : '🌐 '}{s.text}</div>))}</div>)}
    </div>
  );
}

function PeerVideo({ stream }) {
  const ref = useRef(null);
  useEffect(() => { if (ref.current && stream) ref.current.srcObject = stream; }, [stream]);
  return (<div style={{ width: 80, height: 60, borderRadius: 6, overflow: 'hidden', background: '#000' }}><video ref={ref} autoPlay playsInline style={{ width: '100%', height: '100%', objectFit: 'cover' }}/></div>);
}

function ClassificationPanel({ data, onClose }) {
  if (!data) return null;
  const { raceName, currentStage, totalStages, stageClassification, generalClassification } = data;
  const isMulti = totalStages > 1;
  return (
    <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 1500, background: 'rgba(0,0,0,0.85)', overflowY: 'auto', padding: 16 }} onClick={onClose}>
      <div style={{ maxWidth: 400, margin: '20px auto' }} onClick={e => e.stopPropagation()}>
        <div style={{ textAlign: 'center', color: '#fff', marginBottom: 16 }}><div style={{ fontSize: 13, opacity: 0.5 }}>{raceName}</div><h2 style={{ fontSize: 18, color: '#FFD700' }}>📊 Clasificación {isMulti ? `— Etapa ${currentStage}/${totalStages}` : ''}</h2></div>
        <div style={{ background: 'rgba(255,255,255,0.05)', borderRadius: 12, padding: 14, marginBottom: 12, border: '1px solid rgba(255,215,0,0.2)' }}>
          <h3 style={{ fontSize: 13, color: '#FFD700', marginBottom: 8 }}>🚩 {isMulti ? `Etapa ${currentStage} en curso` : raceName || 'Carrera en curso'}</h3>
          <div style={{ fontSize: 9, color: '#888', marginBottom: 6 }}>META = 0 pts · Casilla donde estás = tus puntos</div>
          {stageClassification?.equipos?.map((eq, idx) => (<div key={eq.equipoId} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 8px', background: idx === 0 ? 'rgba(255,215,0,0.08)' : 'transparent', borderRadius: 4 }}><div style={{ width: 20, fontSize: 12, fontWeight: 'bold', color: '#FFD700' }}>{idx === 0 ? '🥇' : idx === 1 ? '🥈' : idx === 2 ? '🥉' : `${idx+1}.`}</div><div style={{ width: 14, height: 14, borderRadius: '50%', background: TEAM_COLORS[eq.equipoId]?.primary }}/><div style={{ flex: 1, color: '#fff', fontSize: 12 }}>{eq.nombre}</div><div style={{ fontSize: 13, fontWeight: 'bold' }}>{eq.puntosEtapa} pts</div></div>))}
          <details style={{ marginTop: 6 }}><summary style={{ fontSize: 10, color: '#888', cursor: 'pointer' }}>Detalle corredores</summary>{stageClassification?.corredores?.map((c, i) => (<div key={c.ciclistaId} style={{ display: 'flex', gap: 4, padding: '2px 6px', fontSize: 9, color: '#aaa' }}><span style={{ width: 16 }}>{i+1}.</span><span style={{ width: 10, height: 10, borderRadius: '50%', background: TEAM_COLORS[c.equipoId]?.primary, display: 'inline-block' }}/><span style={{ flex: 1 }}>#{c.dorsalNum} {c.nombre}</span><span style={{ fontWeight: 'bold', color: c.puntos === 0 ? '#4CAF50' : '#ccc' }}>{c.puntos}</span></div>))}</details>
        </div>
        {isMulti && generalClassification && (<div style={{ background: 'rgba(255,255,255,0.05)', borderRadius: 12, padding: 14, marginBottom: 12, border: '1px solid rgba(76,175,80,0.3)' }}>
          <h3 style={{ fontSize: 13, color: '#4CAF50', marginBottom: 8 }}>🏅 General ({currentStage > 1 ? `${currentStage - 1} etapa${currentStage > 2 ? 's' : ''} completada${currentStage > 2 ? 's' : ''}` : 'Sin etapas completadas'})</h3>
          {generalClassification.length > 0 ? generalClassification.map((eq, idx) => (<div key={eq.equipoId} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 8px', background: idx === 0 ? 'rgba(76,175,80,0.08)' : 'transparent', borderRadius: 4 }}><div style={{ width: 20, fontSize: 12, fontWeight: 'bold' }}>{idx === 0 ? '🥇' : idx === 1 ? '🥈' : idx === 2 ? '🥉' : `${idx+1}.`}</div><div style={{ width: 14, height: 14, borderRadius: '50%', background: TEAM_COLORS[eq.equipoId]?.primary }}/><div style={{ flex: 1, color: '#fff', fontSize: 12 }}>{eq.nombre}{eq.stageWins > 0 && <span style={{ fontSize: 9, opacity: 0.5, marginLeft: 4 }}>{eq.stageWins}🏁</span>}</div><div style={{ fontSize: 13, fontWeight: 'bold', color: idx === 0 ? '#4CAF50' : '#fff' }}>{eq.totalPoints} pts</div></div>)) : <div style={{ fontSize: 11, color: '#888' }}>Aún no hay etapas completadas</div>}
        </div>)}
        <button onClick={onClose} style={{ ...btnStyle('#666'), width: '100%', marginTop: 8 }}>✕ Cerrar clasificación</button>
      </div>
    </div>
  );
}

function StageClassificationScreen({ data, totalStages, raceName, isRaceEnd, winner }) {
  if (!data) return null;
  const { stageResult, classification } = data;
  return (
    <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 2000, background: 'rgba(0,0,0,0.9)', overflowY: 'auto', padding: 16, display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      <div style={{ maxWidth: 400, width: '100%' }}>
        <div style={{ textAlign: 'center', marginBottom: 20, color: '#fff' }}><div style={{ fontSize: 40 }}>{isRaceEnd ? '🏆' : '🏁'}</div><h2 style={{ fontSize: 20, color: '#FFD700', margin: '8px 0' }}>{isRaceEnd ? `¡${raceName} FINALIZADA!` : `Etapa ${stageResult?.stage}/${totalStages} completada`}</h2>{raceName && <div style={{ fontSize: 13, opacity: 0.5 }}>{raceName}</div>}</div>
        <div style={{ background: 'rgba(255,255,255,0.05)', borderRadius: 12, padding: 14, marginBottom: 16, border: '1px solid rgba(255,215,0,0.2)' }}>
          <h3 style={{ fontSize: 14, color: '#FFD700', marginBottom: 10, textAlign: 'center' }}>📊 Clasificación Etapa {stageResult?.stage}</h3>
          <div style={{ fontSize: 10, color: '#aaa', marginBottom: 8, textAlign: 'center' }}>META = 0 pts · Los demás = casilla donde están</div>
          {stageResult?.equipos?.map((eq, idx) => (<div key={eq.equipoId} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', background: idx === 0 ? 'rgba(255,215,0,0.1)' : 'transparent', borderRadius: 6, marginBottom: 4, border: idx === 0 ? '1px solid rgba(255,215,0,0.3)' : 'none' }}><div style={{ fontSize: 16, width: 24, textAlign: 'center', color: '#FFD700', fontWeight: 'bold' }}>{idx === 0 ? '🥇' : idx === 1 ? '🥈' : idx === 2 ? '🥉' : `${idx + 1}º`}</div><div style={{ width: 16, height: 16, borderRadius: '50%', background: TEAM_COLORS[eq.equipoId]?.primary || '#888' }}/><div style={{ flex: 1, color: '#fff', fontSize: 13 }}>{eq.nombre}</div><div style={{ fontSize: 14, fontWeight: 'bold', color: eq.puntosEtapa === 0 ? '#4CAF50' : '#fff' }}>{eq.puntosEtapa} pts</div></div>))}
          <details style={{ marginTop: 8 }}><summary style={{ fontSize: 11, color: '#aaa', cursor: 'pointer' }}>Ver detalle por corredor</summary><div style={{ marginTop: 6 }}>{stageResult?.corredores?.map((c, i) => (<div key={c.ciclistaId} style={{ display: 'flex', gap: 6, padding: '3px 8px', fontSize: 10, color: '#ccc' }}><span style={{ width: 20 }}>{i + 1}.</span><span style={{ width: 12, height: 12, borderRadius: '50%', background: TEAM_COLORS[c.equipoId]?.primary, display: 'inline-block' }}/><span style={{ flex: 1 }}>#{c.dorsalNum} {c.nombre}</span><span style={{ fontWeight: 'bold', color: c.puntos === 0 ? '#4CAF50' : '#fff' }}>{c.puntos}</span></div>))}</div></details>
        </div>
        {totalStages > 1 && classification && (<div style={{ background: 'rgba(255,255,255,0.05)', borderRadius: 12, padding: 14, marginBottom: 16, border: '1px solid rgba(76,175,80,0.3)' }}>
          <h3 style={{ fontSize: 14, color: '#4CAF50', marginBottom: 10, textAlign: 'center' }}>🏅 Clasificación General {isRaceEnd ? 'FINAL' : `(${stageResult?.stage} etapas)`}</h3>
          {classification.map((eq, idx) => (<div key={eq.equipoId} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', background: idx === 0 ? 'rgba(76,175,80,0.1)' : 'transparent', borderRadius: 6, marginBottom: 4, border: idx === 0 ? '1px solid rgba(76,175,80,0.3)' : 'none' }}><div style={{ fontSize: 16, width: 24, textAlign: 'center', fontWeight: 'bold' }}>{idx === 0 ? '🥇' : idx === 1 ? '🥈' : idx === 2 ? '🥉' : `${idx + 1}º`}</div><div style={{ width: 16, height: 16, borderRadius: '50%', background: TEAM_COLORS[eq.equipoId]?.primary || '#888' }}/><div style={{ flex: 1, color: '#fff', fontSize: 13 }}>{eq.nombre}<span style={{ fontSize: 10, opacity: 0.5, marginLeft: 6 }}>{eq.stageWins} etapa{eq.stageWins !== 1 ? 's' : ''}</span></div><div style={{ fontSize: 14, fontWeight: 'bold', color: idx === 0 ? '#4CAF50' : '#fff' }}>{eq.totalPoints} pts</div></div>))}
          {classification[0]?.stagePoints?.length > 1 && (<div style={{ marginTop: 8, fontSize: 10, color: '#aaa', textAlign: 'center' }}>Puntos por etapa: {classification.map(eq => `${eq.nombre?.split(' ')[0]}: ${eq.stagePoints.join('+')}`).join(' | ')}</div>)}
        </div>)}
        <div style={{ textAlign: 'center', color: '#aaa', fontSize: 13 }}>{isRaceEnd ? (<div style={{ color: '#FFD700', fontSize: 16, fontWeight: 'bold' }}>🏆 {winner?.nombre} gana {raceName} con {winner?.totalPoints} puntos</div>) : (<div>⏳ Siguiente etapa en unos segundos...</div>)}</div>
      </div>
    </div>
  );
}

function GameScreen({ socket, gameData, myTeamId, soloMode }) {
  const [tablero, setTablero] = useState(gameData?.tablero || []);
  const [teams, setTeams] = useState(gameData?.teams || []);
  const [currentTurn, setCurrentTurn] = useState(gameData?.currentTurn || null);
  const [selectedCyclist, setSelectedCyclist] = useState(null);
  const [diceValue, setDiceValue] = useState(1);
  const [rolling, setRolling] = useState(false);
  const [eventos, setEventos] = useState([]);
  const [carta, setCarta] = useState(null);
  const [rivalesMontana, setRivalesMontana] = useState(null);
  const [forzarCiclista, setForzarCiclista] = useState(null);
  const [highlightedCell, setHighlightedCell] = useState(null);
  const [winner, setWinner] = useState(null);
  const [phase, setPhase] = useState('roll');
  const [currentStage, setCurrentStage] = useState(gameData?.race?.currentStage || 1);
  const [totalStages, setTotalStages] = useState(gameData?.race?.totalStages || 1);
  const [raceName, setRaceName] = useState(gameData?.race?.name || '');
  const [stageEndData, setStageEndData] = useState(null);
  const [showClassification, setShowClassification] = useState(null);
  const [rivalesDirector, setRivalesDirector] = useState(null);
  const [videochatActive, setVideochatActive] = useState(false);
  const [pendingCiclistaId, setPendingCiclistaId] = useState(null);
  const [userLang, setUserLang] = useState('es');

  const fetchClassification = useCallback(() => { socket.emit('game:getClassification', {}, (res) => { if (res.ok) setShowClassification(res); }); }, [socket]);
  const handleSaveAndExit = useCallback(() => { socket.emit('game:save', {}, (res) => { if (res.ok) { alert(`💾 ${res.message}`); window.location.href = '/game.html'; } else alert(res.error || 'Error'); }); }, [socket]);

  const myTeam = teams.find(t => t.equipoId === myTeamId);
  const isMyTurn = currentTurn === myTeamId;
  const currentTeamName = teams.find(t => t.equipoId === currentTurn)?.nombre_jugador || currentTurn;
  const isMultiStage = totalStages > 1;
  const movibles = useMemo(() => { if (!myTeam) return []; return myTeam.ciclistas.filter(c => !c.bloqueado && !c.pierdeTurno && c.pos < 40); }, [myTeam, teams]);

  useEffect(() => {
    if (!socket) return;
    socket.on('game:diceResult', (data) => { setDiceValue(data.dado); setRolling(false); setPhase('select'); setEventos(prev => [...prev, `🎲 ${data.nombre} tiró: ${data.dado}`].slice(-50)); });
    socket.on('game:turnResult', (data) => { setTeams(data.teams); setPhase('result'); if (data.eventos) setEventos(prev => [...prev, ...data.eventos].slice(-50)); if (data.carta) setCarta(data.carta); if (data.requiereEleccion === 'montana' && data.rivalesMontana && data.equipoId === myTeamId) setRivalesMontana(data.rivalesMontana); if (data.requiereEleccion === 'elegir_equipo_rival' && data.rivalesDirector && data.equipoId === myTeamId) { setRivalesDirector(data.rivalesDirector); setPendingCiclistaId(data.ciclistaId); } if (data.repiteTirada && data.equipoId === myTeamId) { setForzarCiclista(data.forzarMismoCiclista || null); setTimeout(() => setPhase('roll'), 1500); } setHighlightedCell(data.posNueva); setTimeout(() => setHighlightedCell(null), 2000); });
    socket.on('game:stageFinished', (data) => { setStageEndData(data); if (!data.moreStages) setWinner(data.raceWinner); });
    socket.on('game:nextStage', (data) => { setStageEndData(null); setCurrentStage(data.stage); setTablero(data.tablero); setTeams(data.teams); setCurrentTurn(data.currentTurn); setSelectedCyclist(null); setForzarCiclista(null); setPhase('roll'); setEventos(prev => [...prev, `🚩 ETAPA ${data.stage}/${totalStages} — ¡Todos a SALIDA!`]); });
    socket.on('game:montanaResult', (data) => { setTeams(data.teams); setRivalesMontana(null); if (data.mensaje) setEventos(prev => [...prev, data.mensaje].slice(-50)); });
    socket.on('game:directorResult', (data) => { setTeams(data.teams); setRivalesDirector(null); setPendingCiclistaId(null); if (data.mensaje) setEventos(prev => [...prev, data.mensaje].slice(-50)); });
    socket.on('game:nextTurn', (data) => { setCurrentTurn(data.currentTurn); setSelectedCyclist(null); setForzarCiclista(null); setPhase('roll'); });
    socket.on('game:resumed', (data) => { setTablero(data.tablero); setTeams(data.teams); setCurrentTurn(data.currentTurn); setCurrentStage(data.race.currentStage); setTotalStages(data.race.totalStages); setRaceName(data.race.name); setSelectedCyclist(null); setForzarCiclista(null); setPhase('roll'); setEventos(prev => [...prev, `▶️ ¡Partida reanudada! Etapa ${data.race.currentStage}/${data.race.totalStages}`]); });
    socket.on('game:autosaved', (data) => { setEventos(prev => [...prev, `💾 Partida auto-guardada (${data.disconnected} se desconectó)`]); });
    socket.on('game:saved', (data) => { setEventos(prev => [...prev, `💾 Partida guardada — ${data.raceName} etapa ${data.currentStage}/${data.totalStages}`]); });
    return () => { socket.off('game:diceResult'); socket.off('game:turnResult'); socket.off('game:stageFinished'); socket.off('game:nextStage'); socket.off('game:montanaResult'); socket.off('game:directorResult'); socket.off('game:nextTurn'); socket.off('game:resumed'); socket.off('game:autosaved'); socket.off('game:saved'); };
  }, [socket, myTeamId, totalStages]);

  const handleRoll = useCallback(() => { if (!isMyTurn || rolling) return; setRolling(true); const interval = setInterval(() => setDiceValue(Math.floor(Math.random() * 6) + 1), 80); setTimeout(() => { clearInterval(interval); socket.emit('game:rollDice', {}, (res) => { if (!res.ok) { setRolling(false); setPhase('roll'); setEventos(prev => [...prev, `❌ ${res.error}`]); } }); }, 800); }, [isMyTurn, rolling, socket]);
  const handleSelectCyclist = useCallback((ciclistaId) => { setSelectedCyclist(ciclistaId); socket.emit('game:selectCyclist', { ciclistaId }, (res) => { if (!res.ok) { setEventos(prev => [...prev, `❌ ${res.error}`]); setPhase('select'); } }); }, [socket]);
  const handleMontanaChoice = useCallback((rivalEquipoId, rivalCiclistaId) => { socket.emit('game:montanaChoice', { rivalEquipoId, rivalCiclistaId }, (res) => { if (!res.ok) setEventos(prev => [...prev, `❌ ${res.error}`]); }); }, [socket]);
  const handleDirectorChoice = useCallback((rivalEquipoId) => { socket.emit('game:directorChoice', { rivalEquipoId, ciclistaId: pendingCiclistaId }, (res) => { if (!res.ok) setEventos(prev => [...prev, `❌ ${res.error}`]); setRivalesDirector(null); setPendingCiclistaId(null); }); }, [socket, pendingCiclistaId]);

  if (winner) {
    return (<div style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #1a1a2e, #16213e)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#fff', padding: 20 }}><div style={{ fontSize: 64, marginBottom: 20 }}>🏆</div><h1 style={{ fontSize: 28, color: '#FFD700' }}>¡VICTORIA!</h1><div style={{ fontSize: 22, marginTop: 12, padding: '12px 24px', borderRadius: 12, background: TEAM_COLORS[winner.equipoId]?.primary || '#888' }}>{winner.jersey} {winner.nombre} ({winner.color})</div><div style={{ marginTop: 20, fontSize: 14, opacity: 0.7 }}>Primer ciclista en META</div><EventLog eventos={eventos} /></div>);
  }

  return (
    <div style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #0f0f23, #1a1a2e)', color: '#fff', padding: '8px 4px', fontFamily: "'Segoe UI', sans-serif" }}>
      <div style={{ textAlign: 'center', padding: '8px 12px', marginBottom: 8, background: isMyTurn ? 'rgba(76,175,80,0.2)' : 'rgba(255,255,255,0.05)', borderRadius: 8, border: isMyTurn ? '1px solid #4CAF50' : '1px solid rgba(255,255,255,0.1)' }}>
        <div style={{ fontSize: 11, opacity: 0.6 }}>{isMultiStage ? `${raceName} — Etapa ${currentStage}/${totalStages}` : 'CICLISMO DE MESA ONLINE'}</div>
        <div style={{ fontSize: 15, fontWeight: 'bold', color: isMyTurn ? '#4CAF50' : '#aaa' }}>{isMyTurn ? '🎲 ¡TU TURNO!' : `⏳ Turno de ${currentTeamName}`}</div>
        {forzarCiclista && <div style={{ fontSize: 11, color: '#FDD835' }}>⚡ Repite tirada — mismo corredor</div>}
        <div style={{ display: 'flex', gap: 6, justifyContent: 'center', marginTop: 6 }}>
          <button onClick={fetchClassification} style={{ padding: '4px 14px', background: 'rgba(255,215,0,0.15)', border: '1px solid rgba(255,215,0,0.3)', borderRadius: 6, color: '#FFD700', fontSize: 11, cursor: 'pointer', fontWeight: 'bold' }}>📊 Clasificación</button>
          {isMultiStage && <button onClick={handleSaveAndExit} style={{ padding: '4px 14px', background: 'rgba(142,36,170,0.15)', border: '1px solid rgba(142,36,170,0.3)', borderRadius: 6, color: '#CE93D8', fontSize: 11, cursor: 'pointer', fontWeight: 'bold' }}>💾 Guardar y salir</button>}
        </div>
      </div>
      {showClassification && <ClassificationPanel data={showClassification} onClose={() => setShowClassification(null)} />}
      {stageEndData && <StageClassificationScreen data={stageEndData} totalStages={totalStages} raceName={raceName} isRaceEnd={!stageEndData.moreStages} winner={winner} />}
      <GameBoard tablero={tablero} teams={teams} highlightedCell={highlightedCell} />
      <div style={{ maxWidth: 400, margin: '12px auto', display: 'flex', flexDirection: 'column', gap: 10 }}>
        {isMyTurn && phase === 'roll' && (<div style={{ textAlign: 'center' }}><DiceRoller value={diceValue} rolling={rolling} onRoll={handleRoll} disabled={rolling} />{forzarCiclista && <div style={{ fontSize: 12, color: '#FDD835', marginTop: 4 }}>⚡ Repite tirada — mismo corredor</div>}</div>)}
        {isMyTurn && phase === 'select' && (<div><div style={{ textAlign: 'center', fontSize: 24, fontWeight: 'bold', color: '#FFD700', marginBottom: 8 }}>🎲 Has sacado: {diceValue}</div><CyclistSelector team={myTeam} movibles={movibles} forzarCiclista={forzarCiclista} onSelect={handleSelectCyclist} selectedId={selectedCyclist} /></div>)}
        {(!isMyTurn || phase === 'result') && diceValue > 0 && (<div style={{ textAlign: 'center' }}><DiceRoller value={diceValue} rolling={false} disabled /></div>)}
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6 }}>
          {teams.map(t => { const best = Math.max(...t.ciclistas.map(c => c.pos)); return (<div key={t.equipoId} style={{ background: currentTurn === t.equipoId ? 'rgba(255,255,255,0.1)' : 'rgba(0,0,0,0.2)', borderRadius: 6, padding: 6, textAlign: 'center', border: `2px solid ${TEAM_COLORS[t.equipoId]?.primary || '#888'}`, opacity: currentTurn === t.equipoId ? 1 : 0.7 }}><div style={{ fontSize: 11, fontWeight: 'bold', color: TEAM_COLORS[t.equipoId]?.primary }}>{t.isBot ? '🤖' : '👤'} {t.nombre_jugador}</div><div style={{ fontSize: 10, opacity: 0.6 }}>Mejor: cas.{40 - best}</div></div>); })}
        </div>
        <EventLog eventos={eventos} />
      </div>
      {carta && <CardPopup carta={carta} onClose={() => setCarta(null)} />}
      {rivalesMontana && <MontanaSelector rivales={rivalesMontana} onSelect={handleMontanaChoice} />}
      {rivalesDirector && <DirectorChoiceSelector rivales={rivalesDirector} onSelect={handleDirectorChoice} />}
      {!soloMode && (<div style={{ maxWidth: 400, margin: '0 auto' }}>
        {!videochatActive ? (<div style={{ display: 'flex', gap: 6, marginTop: 8 }}><button onClick={() => setVideochatActive(true)} style={{ flex: 1, padding: '8px', background: 'rgba(30,136,229,0.15)', border: '1px solid rgba(30,136,229,0.3)', borderRadius: 8, color: '#1E88E5', fontSize: 12, cursor: 'pointer' }}>📹 Activar videochat multilingüe</button><LanguageSelector selectedLang={userLang} onSelect={(l) => { setUserLang(l); socket.emit('voice:setLang', { lang: l }); }} compact /></div>) : (<div><div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}><LanguageSelector selectedLang={userLang} onSelect={(l) => { setUserLang(l); socket.emit('voice:setLang', { lang: l }); }} compact /><button onClick={() => setVideochatActive(false)} style={{ padding: '3px 8px', borderRadius: 4, border: 'none', background: '#666', color: '#fff', fontSize: 10, cursor: 'pointer' }}>✕ Cerrar</button></div><VideoChatPanel socket={socket} active={videochatActive} /></div>)}
      </div>)}
      <style>{`@keyframes diceShake { 0% { transform: rotate(0deg) scale(1.05); } 25% { transform: rotate(-8deg) scale(1.1); } 50% { transform: rotate(8deg) scale(1.05); } 75% { transform: rotate(-4deg) scale(1.1); } 100% { transform: rotate(0deg) scale(1.05); } }`}</style>
    </div>
  );
}

function InviteScreen({ socket, inviteToken, onGameStart }) {
  const [inviteInfo, setInviteInfo] = useState(null); const [email, setEmail] = useState(''); const [name, setName] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [step, setStep] = useState('loading');
  useEffect(() => { fetch(`/api/invite/${inviteToken}`).then(r => r.json()).then(data => { setLoading(false); if (data.ok) { setInviteInfo(data); setStep('register'); } else { setError(data.error); setStep('error'); } }).catch(() => { setLoading(false); setError('Error de conexión'); setStep('error'); }); }, [inviteToken]);
  const handleJoin = () => { if (!email || !name) return; setLoading(true); socket.emit('invite:use', { token: inviteToken, email: email.toLowerCase().trim() }, (res) => { setLoading(false); if (!res.ok) { setError(res.error); setStep(res.requiresMembership ? 'needMembership' : 'error'); return; } socket.emit('player:setName', { name }, () => {}); socket.emit('room:ready', { ready: true }); onGameStart(null, res.teamId, false, res.room); }); };
  return (
    <div style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #0f0f23, #1a1a2e)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#fff', padding: 20, fontFamily: "'Segoe UI', sans-serif" }}>
      <div style={{ fontSize: 48, marginBottom: 8 }}>🎫</div><h2 style={{ fontSize: 20, margin: '0 0 20px' }}>Invitación a partida</h2>
      {step === 'loading' && <div>Verificando invitación...</div>}
      {step === 'register' && inviteInfo && (<div style={{ width: 300, display: 'flex', flexDirection: 'column', gap: 14 }}><div style={{ background: 'rgba(76,175,80,0.1)', borderRadius: 10, padding: 14, border: '1px solid #4CAF50', textAlign: 'center' }}><div style={{ fontSize: 13, opacity: 0.7 }}>Te invita a jugar:</div><div style={{ fontSize: 18, fontWeight: 'bold', color: '#4CAF50' }}>{inviteInfo.hostName}</div><div style={{ fontSize: 12, opacity: 0.5, marginTop: 4 }}>{inviteInfo.teamCount}/{inviteInfo.maxTeams} equipos en sala</div></div><div><label style={{ fontSize: 12, opacity: 0.6 }}>Tu email:</label><input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="tu@email.com" style={inputStyle} /></div><div><label style={{ fontSize: 12, opacity: 0.6 }}>Tu nombre de ciclista:</label><input type="text" value={name} onChange={e => setName(e.target.value)} placeholder="Indurain, Merckx..." style={inputStyle} maxLength={24} /></div><button onClick={handleJoin} disabled={loading || !email || !name} style={btnStyle('#4CAF50')}>{loading ? '⏳ Entrando...' : '🚴 Unirme a la partida'}</button><div style={{ fontSize: 10, opacity: 0.4, textAlign: 'center' }}>Al unirte aceptas registrarte en Ciclismo de Mesa Online</div></div>)}
      {step === 'needMembership' && (<div style={{ textAlign: 'center', maxWidth: 300 }}><div style={{ fontSize: 48, marginBottom: 12 }}>🔒</div><p style={{ marginBottom: 16 }}>Has agotado tus 10 partidas por invitación.</p><p style={{ fontSize: 14, opacity: 0.7, marginBottom: 20 }}>Compra la membresía para seguir jugando sin límites.</p><button onClick={() => window.location.href = '/'} style={btnStyle('#FB8C00')}>Ver membresía</button></div>)}
      {step === 'error' && (<div style={{ textAlign: 'center' }}><div style={{ fontSize: 48, marginBottom: 12 }}>❌</div><p>{error}</p><button onClick={() => window.location.href = '/game.html'} style={{ ...btnStyle('#666'), marginTop: 16 }}>Ir al juego</button></div>)}
    </div>
  );
}

function InviteShareButton({ socket, onInviteCreated }) {
  const [inviteUrl, setInviteUrl] = useState(null); const [remaining, setRemaining] = useState(3); const [loading, setLoading] = useState(false); const [copied, setCopied] = useState(false);
  const createInvite = () => { setLoading(true); socket.emit('invite:create', {}, (res) => { setLoading(false); if (res.ok) { setInviteUrl(res.url); setRemaining(res.remaining); if (onInviteCreated) onInviteCreated(res); } else alert(res.error || 'Error al crear invitación'); }); };
  const copyLink = () => { if (inviteUrl) navigator.clipboard?.writeText(inviteUrl).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }); };
  const shareLink = () => { if (inviteUrl && navigator.share) navigator.share({ title: 'Ciclismo de Mesa Online', text: '¡Te invito a jugar conmigo!', url: inviteUrl }).catch(() => copyLink()); else copyLink(); };
  if (inviteUrl) return (<div style={{ background: 'rgba(76,175,80,0.1)', borderRadius: 8, padding: 10, marginTop: 8 }}><div style={{ fontSize: 11, color: '#4CAF50', marginBottom: 6 }}>🎫 Enlace de invitación:</div><div style={{ fontSize: 10, color: '#aaa', background: 'rgba(0,0,0,0.3)', padding: 6, borderRadius: 4, wordBreak: 'break-all', marginBottom: 8 }}>{inviteUrl}</div><div style={{ display: 'flex', gap: 6 }}><button onClick={shareLink} style={{ ...btnStyle('#4CAF50'), padding: '8px 12px', fontSize: 12, flex: 1 }}>{copied ? '✅ Copiado' : '📤 Compartir'}</button>{remaining > 0 && <button onClick={() => { setInviteUrl(null); createInvite(); }} style={{ ...btnStyle('#1E88E5'), padding: '8px 12px', fontSize: 12 }}>+Otro ({remaining})</button>}</div></div>);
  return (<button onClick={createInvite} disabled={loading} style={{ ...btnStyle('#4CAF50'), fontSize: 13, padding: '10px 16px' }}>{loading ? '⏳...' : '🎫 Invitar jugador (máx 3)'}</button>);
}

function InviteBalancePanel({ socket, userInfo }) {
  const [status, setStatus] = useState(null);
  useEffect(() => { if (!socket) return; socket.emit('invite:status', {}, (res) => { if (res.ok) setStatus(res); }); }, [socket, userInfo]);
  if (!status || status.isMember) return null;
  const remaining = typeof status.remaining === 'number' ? status.remaining : 0; const used = status.inviteUses || 0; const max = status.maxInviteUses || 10; const pct = Math.round((remaining / max) * 100); const isExhausted = remaining <= 0;
  return (
    <div style={{ background: isExhausted ? 'rgba(244,67,54,0.1)' : 'rgba(255,215,0,0.08)', border: `1px solid ${isExhausted ? '#f44336' : 'rgba(255,215,0,0.3)'}`, borderRadius: 12, padding: 16, marginBottom: 16, width: '100%', maxWidth: 320 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}><div style={{ fontSize: 14, fontWeight: 'bold', color: '#fff' }}>🎫 Partidas por invitación</div><div style={{ fontSize: 20, fontWeight: 'bold', color: isExhausted ? '#f44336' : remaining <= 3 ? '#FB8C00' : '#4CAF50' }}>{remaining}/{max}</div></div>
      <div style={{ width: '100%', height: 8, background: 'rgba(255,255,255,0.1)', borderRadius: 4, overflow: 'hidden', marginBottom: 10 }}><div style={{ width: `${pct}%`, height: '100%', borderRadius: 4, transition: 'width 0.5s', background: isExhausted ? '#f44336' : remaining <= 3 ? '#FB8C00' : '#4CAF50' }}/></div>
      {!isExhausted && <div style={{ fontSize: 12, color: '#aaa', lineHeight: 1.5 }}>Te pueden invitar a partidas. Cada invitación aceptada resta 1 de tu saldo.<span style={{ color: '#FFD700' }}> Has usado {used} de {max}.</span></div>}
      {isExhausted && (<div><div style={{ fontSize: 13, color: '#f44336', fontWeight: 'bold', marginBottom: 10, lineHeight: 1.4 }}>❌ Has agotado tus {max} partidas gratuitas por invitación. Para seguir jugando necesitas comprar la membresía.</div><button onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} style={{ ...btnStyle('#FFD700'), color: '#000', width: '100%', padding: '12px 16px', fontSize: 14 }}>💎 Comprar membresía — 25€ + IVA / año</button></div>)}
      {!isExhausted && remaining <= 3 && <div style={{ marginTop: 10, padding: 8, background: 'rgba(251,140,0,0.1)', borderRadius: 6, fontSize: 11, color: '#FB8C00', textAlign: 'center' }}>⚠️ Te quedan pocas partidas gratis. Compra membresía para jugar sin límites.</div>}
    </div>
  );
}

function AuthScreen({ socket, onAuth }) {
  const [email, setEmail] = useState(''); const [name, setName] = useState(''); const [lang, setLang] = useState('es'); const [loading, setLoading] = useState(false); const [error, setError] = useState(null);
  const handleLogin = () => { if (!email) return; setLoading(true); setError(null); socket.emit('auth:login', { email: email.toLowerCase().trim() }, (res) => { if (!res.ok) { setLoading(false); setError(res.error); return; } if (name.trim()) socket.emit('player:setName', { name: name.trim() }, () => {}); socket.emit('voice:setLang', { lang }); setLoading(false); onAuth({ ...res.user, lang }); }); };
  return (
    <div style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #0f0f23, #1a1a2e)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#fff', padding: 20, fontFamily: "'Segoe UI', sans-serif" }}>
      <div style={{ fontSize: 48, marginBottom: 8 }}>🚴</div><h1 style={{ fontSize: 22, margin: '0 0 4px', textAlign: 'center' }}>CICLISMO DE MESA ONLINE</h1><p style={{ opacity: 0.5, fontSize: 13, margin: '0 0 24px' }}>Regístrate para jugar</p>
      <div style={{ width: 300, display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div><label style={{ fontSize: 12, opacity: 0.6 }}>Email:</label><input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="tu@email.com" style={inputStyle} onKeyDown={e => e.key === 'Enter' && handleLogin()} /></div>
        <div><label style={{ fontSize: 12, opacity: 0.6 }}>Nombre de ciclista:</label><input type="text" value={name} onChange={e => setName(e.target.value)} placeholder="Indurain, Merckx..." style={inputStyle} maxLength={24} onKeyDown={e => e.key === 'Enter' && handleLogin()} /></div>
        <LanguageSelector selectedLang={lang} onSelect={setLang} />
        {error && <div style={{ color: '#f44', fontSize: 13 }}>{error}</div>}
        <button onClick={handleLogin} disabled={loading || !email} style={btnStyle('#4CAF50')}>{loading ? '⏳ Entrando...' : '🚴 Entrar'}</button>
      </div>
    </div>
  );
}

function SavedGamesPanel({ socket, onResume, onClose }) {
  const [games, setGames] = useState([]); const [loading, setLoading] = useState(true);
  useEffect(() => { socket.emit('game:mySavedGames', {}, (res) => { setLoading(false); if (res.ok) setGames(res.games); }); }, [socket]);
  const handleResume = (code) => { socket.emit('game:resume', { code }, (res) => { if (res.ok) onResume(res); else alert(res.error || 'Error al reanudar'); }); };
  const formatDate = (ts) => { if (!ts) return ''; const d = new Date(ts); return `${d.getDate()}/${d.getMonth()+1} ${d.getHours()}:${String(d.getMinutes()).padStart(2,'0')}`; };
  return (
    <div style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 2000, background: 'rgba(0,0,0,0.9)', overflowY: 'auto', padding: 20, display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      <div style={{ maxWidth: 400, width: '100%' }}>
        <h2 style={{ color: '#FFD700', textAlign: 'center', fontSize: 18, marginBottom: 16 }}>💾 Carreras guardadas</h2>
        {loading && <div style={{ textAlign: 'center', color: '#aaa' }}>Cargando...</div>}
        {!loading && games.length === 0 && <div style={{ textAlign: 'center', color: '#888', padding: 30 }}>No tienes carreras guardadas</div>}
        {games.map(g => (<div key={g.code} style={{ background: 'rgba(255,255,255,0.05)', borderRadius: 10, padding: 14, marginBottom: 10, border: '1px solid rgba(255,215,0,0.2)' }}><div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }}><div><div style={{ color: '#FFD700', fontSize: 15, fontWeight: 'bold' }}>{g.raceName}</div><div style={{ fontSize: 11, color: '#aaa' }}>Etapa {g.currentStage}/{g.totalStages} · {g.raceFormat === 'semana' ? '📅 Semana' : '🏆 Grand Tour'}</div></div><div style={{ fontSize: 10, color: '#666' }}>{formatDate(g.savedAt)}</div></div><div style={{ display: 'flex', gap: 4, marginBottom: 8, flexWrap: 'wrap' }}>{g.teams.map(t => (<div key={t.equipoId} style={{ padding: '2px 8px', borderRadius: 10, fontSize: 10, background: TEAM_COLORS[t.equipoId]?.primary || '#888', color: '#fff' }}>{t.isBot ? '🤖' : '👤'} {t.nombre}</div>))}</div><button onClick={() => handleResume(g.code)} style={{ ...btnStyle('#4CAF50'), width: '100%', padding: '10px', fontSize: 13 }}>▶️ Continuar carrera</button></div>))}
        <button onClick={onClose} style={{ ...btnStyle('#666'), width: '100%', marginTop: 10 }}>← Volver</button>
      </div>
    </div>
  );
}

function RaceFormatSelector({ raceFormat, setRaceFormat, raceName, setRaceName }) {
  const formats = [{ id: 'clasica', label: '🏅 Clásica', sub: '1 etapa' }, { id: 'semana', label: '📅 Semana', sub: '7 etapas' }, { id: 'grand_tour', label: '🏆 Grand Tour', sub: '21 etapas' }];
  const placeholders = { clasica: 'Clásica de Yaiza, París-Roubaix...', semana: 'Semana de Yaiza, Dauphiné...', grand_tour: 'Tour de Yaiza, Vuelta a Lanzarote...' };
  return (<div><label style={{ fontSize: 13, opacity: 0.7 }}>Formato de carrera:</label><div style={{ display: 'flex', gap: 6, marginTop: 6 }}>{formats.map(f => (<div key={f.id} onClick={() => setRaceFormat(f.id)} style={{ flex: 1, padding: '10px 6px', borderRadius: 8, textAlign: 'center', cursor: 'pointer', background: raceFormat === f.id ? 'rgba(255,215,0,0.15)' : 'rgba(255,255,255,0.03)', border: raceFormat === f.id ? '2px solid #FFD700' : '1px solid rgba(255,255,255,0.1)', transition: 'all 0.2s' }}><div style={{ fontSize: 14, fontWeight: 'bold', color: raceFormat === f.id ? '#FFD700' : '#fff' }}>{f.label}</div><div style={{ fontSize: 10, opacity: 0.5 }}>{f.sub}</div></div>))}</div><div style={{ marginTop: 10 }}><label style={{ fontSize: 12, opacity: 0.6 }}>Nombre de la carrera:</label><input type="text" value={raceName} onChange={e => setRaceName(e.target.value)} placeholder={placeholders[raceFormat]} style={inputStyle} maxLength={40} /></div></div>);
}

function LobbyScreen({ socket, onGameStart, userInfo }) {
  const [mode, setMode] = useState(null); const [numBots, setNumBots] = useState(3); const [teamId, setTeamId] = useState('rojo'); const [loading, setLoading] = useState(false); const [roomCode, setRoomCode] = useState(null); const [roomData, setRoomData] = useState(null); const [joinCode, setJoinCode] = useState(''); const [raceFormat, setRaceFormat] = useState('clasica'); const [raceName, setRaceName] = useState(''); const [showSavedGames, setShowSavedGames] = useState(false); const [botDifficulty, setBotDifficulty] = useState('dificil');
  useEffect(() => { if (!socket) return; socket.on('room:playerJoined', (data) => { setRoomData(prev => prev ? { ...prev, teams: [...(prev.teams || []), data.team], teamCount: data.teamCount } : prev); }); socket.on('game:started', (data) => { onGameStart(data, null, false); }); return () => { socket.off('room:playerJoined'); socket.off('game:started'); }; }, [socket]);
  const startSolo = () => { setLoading(true); socket.emit('game:solo', { numBots, turnTimer: 30, equipoId: teamId, raceFormat, raceName, botDifficulty }, (res) => { setLoading(false); if (res.ok) onGameStart(res.gameData, res.teamId, true); else alert(res.error || 'Error'); }); };
  const createRoom = () => { setLoading(true); socket.emit('room:create', { numTeams: 6, turnTimer: 30, raceFormat, raceName }, (res) => { if (!res.ok) { setLoading(false); alert(res.error || 'Error'); return; } const code = res.code; socket.emit('room:join', { code, equipoId: teamId }, (joinRes) => { setLoading(false); if (joinRes.ok) { setRoomCode(code); setRoomData(joinRes.room); socket.emit('room:ready', { ready: true }); } else alert(joinRes.error || 'Error'); }); }); };
  const startOnlineGame = () => { socket.emit('room:start', {}, (res) => { if (!res.ok) alert(res.error || 'Error'); }); };
  const addBot = () => { socket.emit('room:addBots', { count: 1 }, () => {}); };
  return (
    <div style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #0f0f23, #1a1a2e)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#fff', padding: 20, fontFamily: "'Segoe UI', sans-serif" }}>
      <div style={{ fontSize: 48, marginBottom: 8 }}>🚴</div><h1 style={{ fontSize: 24, margin: '0 0 4px', textAlign: 'center' }}>CICLISMO DE MESA ONLINE</h1><p style={{ opacity: 0.5, fontSize: 13, margin: '0 0 16px' }}>Juegos Garate — Grupo Yaiza</p>
      {userInfo && (<div style={{ marginBottom: 16, textAlign: 'center' }}><div style={{ fontSize: 13, color: '#aaa', marginBottom: 4 }}>👤 {userInfo.cyclistName || userInfo.email}{userInfo.membership?.active && <span style={{ marginLeft: 8, padding: '2px 8px', background: '#FFD700', color: '#000', borderRadius: 10, fontSize: 10, fontWeight: 'bold' }}>💎 MIEMBRO</span>}</div></div>)}
      <InviteBalancePanel socket={socket} userInfo={userInfo} />
      {!mode && (<div style={{ display: 'flex', flexDirection: 'column', gap: 12, width: 260 }}><button onClick={() => setMode('solo')} style={btnStyle('#4CAF50')}>🤖 Solo vs IA</button><button onClick={() => setMode('create')} style={btnStyle('#1E88E5')}>🌐 Crear partida online</button><button onClick={() => setMode('join')} style={btnStyle('#FB8C00')}>🔗 Unirse con código</button><button onClick={() => setShowSavedGames(true)} style={btnStyle('#8E24AA')}>💾 Carreras guardadas</button></div>)}
      {mode === 'solo' && (<div style={{ width: 300, display: 'flex', flexDirection: 'column', gap: 16 }}>
        <div><label style={{ fontSize: 13, opacity: 0.7 }}>Tu equipo:</label><div style={{ display: 'flex', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>{Object.entries(TEAM_COLORS).map(([id, colors]) => (<div key={id} onClick={() => setTeamId(id)} style={{ width: 40, height: 40, borderRadius: 8, background: colors.primary, border: teamId === id ? '3px solid #fff' : '2px solid transparent', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, color: '#fff', fontWeight: 'bold' }}>{id.slice(0,3).toUpperCase()}</div>))}</div></div>
        <div><label style={{ fontSize: 13, opacity: 0.7 }}>Rivales IA: {numBots}</label><input type="range" min={1} max={5} value={numBots} onChange={e => setNumBots(+e.target.value)} style={{ width: '100%' }} /></div>
        <div><label style={{ fontSize: 13, opacity: 0.7 }}>Nivel de la IA:</label><div style={{ display: 'flex', gap: 6, marginTop: 6 }}>{[{ id: 'facil', label: '😊 Fácil', sub: 'Elige al azar', color: '#4CAF50' }, { id: 'dificil', label: '😤 Difícil', sub: 'Juega a ganar', color: '#FB8C00' }, { id: 'extremo', label: '🔥 Extremo', sub: 'Analiza todo', color: '#f44336' }].map(d => (<div key={d.id} onClick={() => setBotDifficulty(d.id)} style={{ flex: 1, padding: '8px 4px', borderRadius: 8, textAlign: 'center', cursor: 'pointer', background: botDifficulty === d.id ? `${d.color}22` : 'rgba(255,255,255,0.03)', border: botDifficulty === d.id ? `2px solid ${d.color}` : '1px solid rgba(255,255,255,0.1)', transition: 'all 0.2s' }}><div style={{ fontSize: 13, fontWeight: 'bold', color: botDifficulty === d.id ? d.color : '#fff' }}>{d.label}</div><div style={{ fontSize: 9, opacity: 0.5 }}>{d.sub}</div></div>))}</div></div>
        <RaceFormatSelector raceFormat={raceFormat} setRaceFormat={setRaceFormat} raceName={raceName} setRaceName={setRaceName} />
        <button onClick={startSolo} disabled={loading} style={btnStyle('#4CAF50')}>{loading ? '⏳ Creando...' : `🏁 ${raceFormat === 'clasica' ? 'Empezar clásica' : raceFormat === 'semana' ? 'Empezar semana (7 etapas)' : 'Empezar grand tour (21 etapas)'}`}</button>
        <button onClick={() => setMode(null)} style={btnStyle('#666')}>← Volver</button>
      </div>)}
      {mode === 'create' && !roomCode && (<div style={{ width: 300, display: 'flex', flexDirection: 'column', gap: 16 }}>
        <div><label style={{ fontSize: 13, opacity: 0.7 }}>Tu equipo:</label><div style={{ display: 'flex', gap: 8, marginTop: 6, flexWrap: 'wrap' }}>{Object.entries(TEAM_COLORS).map(([id, colors]) => (<div key={id} onClick={() => setTeamId(id)} style={{ width: 40, height: 40, borderRadius: 8, background: colors.primary, border: teamId === id ? '3px solid #fff' : '2px solid transparent', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10, color: '#fff', fontWeight: 'bold' }}>{id.slice(0,3).toUpperCase()}</div>))}</div></div>
        <RaceFormatSelector raceFormat={raceFormat} setRaceFormat={setRaceFormat} raceName={raceName} setRaceName={setRaceName} />
        <button onClick={createRoom} disabled={loading} style={btnStyle('#1E88E5')}>{loading ? '⏳ Creando...' : '🌐 Crear sala'}</button><button onClick={() => setMode(null)} style={btnStyle('#666')}>← Volver</button>
      </div>)}
      {mode === 'create' && roomCode && (<div style={{ width: 320, display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div style={{ background: 'rgba(30,136,229,0.15)', borderRadius: 10, padding: 14, border: '1px solid #1E88E5', textAlign: 'center' }}><div style={{ fontSize: 12, opacity: 0.6 }}>Código de sala:</div><div style={{ fontSize: 32, fontWeight: 'bold', letterSpacing: 4, color: '#1E88E5' }}>{roomCode}</div><div style={{ fontSize: 11, opacity: 0.5, marginTop: 4 }}>{roomData?.teams?.length || 1} equipo(s) en sala</div></div>
        {roomData?.teams?.map(t => (<div key={t.equipoId} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: 8, background: 'rgba(255,255,255,0.05)', borderRadius: 6, border: `1px solid ${TEAM_COLORS[t.equipoId]?.primary || '#888'}` }}><div style={{ width: 24, height: 24, borderRadius: '50%', background: TEAM_COLORS[t.equipoId]?.primary }}/><div style={{ flex: 1, fontSize: 13 }}>{t.isBot ? '🤖' : '👤'} {t.nombre_jugador}</div><div style={{ fontSize: 10, opacity: 0.5 }}>{t.nombre}</div></div>))}
        <InviteShareButton socket={socket} />
        <div style={{ display: 'flex', gap: 8 }}><button onClick={addBot} style={{ ...btnStyle('#666'), flex: 1, padding: '10px 8px', fontSize: 12 }}>🤖 Añadir IA</button><button onClick={startOnlineGame} disabled={(roomData?.teams?.length || 1) < 2} style={{ ...btnStyle('#4CAF50'), flex: 1, padding: '10px 8px', fontSize: 12 }}>🏁 Empezar</button></div>
      </div>)}
      {mode === 'join' && (<div style={{ width: 300, display: 'flex', flexDirection: 'column', gap: 16 }}><div><label style={{ fontSize: 13, opacity: 0.7 }}>Código de sala:</label><input type="text" value={joinCode} onChange={e => setJoinCode(e.target.value.toUpperCase())} placeholder="XXXXXX" maxLength={6} style={{ ...inputStyle, textAlign: 'center', fontSize: 24, letterSpacing: 4 }} /></div><button onClick={() => { socket.emit('room:join', { code: joinCode }, (res) => { if (res.ok) onGameStart(null, res.teamId, false, res.room); else alert(res.error || 'Error'); }); }} disabled={joinCode.length < 6} style={btnStyle('#FB8C00')}>🔗 Unirme</button><button onClick={() => setMode(null)} style={btnStyle('#666')}>← Volver</button></div>)}
      {showSavedGames && <SavedGamesPanel socket={socket} onResume={(res) => { setShowSavedGames(false); onGameStart(null, res.teamId, false); }} onClose={() => setShowSavedGames(false)} />}
    </div>
  );
}

const inputStyle = { width: '100%', padding: '12px 14px', background: 'rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.2)', borderRadius: 8, color: '#fff', fontSize: 15, outline: 'none', marginTop: 4 };
const btnStyle = (bg) => ({ padding: '14px 24px', background: bg, color: '#fff', border: 'none', borderRadius: 10, fontSize: 16, fontWeight: 'bold', cursor: 'pointer', boxShadow: '0 2px 8px rgba(0,0,0,0.3)' });

function App() {
  const [socket, setSocket] = useState(null); const [connected, setConnected] = useState(false); const [screen, setScreen] = useState('auth'); const [gameData, setGameData] = useState(null); const [myTeamId, setMyTeamId] = useState(null); const [soloMode, setSoloMode] = useState(false); const [inviteToken, setInviteToken] = useState(null); const [userInfo, setUserInfo] = useState(null);
  useEffect(() => { const params = new URLSearchParams(window.location.search); const token = params.get('invite'); if (token) setInviteToken(token); }, []);
  useEffect(() => { const s = io({ transports: ['polling', 'websocket'], upgrade: true }); s.on('connect', () => setConnected(true)); s.on('disconnect', () => setConnected(false)); setSocket(s); return () => s.disconnect(); }, []);
  const handleAuth = (user) => { setUserInfo(user); setScreen('lobby'); };
  const handleGameStart = (data, teamId, solo, roomData) => { setGameData(data || roomData); if (teamId) setMyTeamId(teamId); setSoloMode(solo); setScreen('game'); if (inviteToken) window.history.replaceState({}, '', '/game.html'); };
  if (!connected) return (<div style={{ minHeight: '100vh', background: '#0f0f23', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff' }}><div style={{ textAlign: 'center' }}><div style={{ fontSize: 48, marginBottom: 16 }}>🚴</div><div>Conectando...</div></div></div>);
  if (inviteToken && screen !== 'game') return <InviteScreen socket={socket} inviteToken={inviteToken} onGameStart={handleGameStart} />;
  if (screen === 'auth') return <AuthScreen socket={socket} onAuth={handleAuth} />;
  if (screen === 'game' && gameData) return <GameScreen socket={socket} gameData={gameData} myTeamId={myTeamId} soloMode={soloMode} />;
  return <LobbyScreen socket={socket} onGameStart={handleGameStart} userInfo={userInfo} />;
}

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