import React, { createContext, useContext, useRef, useState, useCallback, useMemo, useEffect } from 'react';
import { io } from 'socket.io-client';
import { API_CONFIG } from '../api/userApi';

const GameWebSocketContext = createContext(null);

const isDev = import.meta.env.DEV;
const log = (...args) => isDev && console.log(...args);
const warn = (...args) => isDev && console.warn(...args);
const logError = (...args) => console.error(...args);

/**
 * Provider that maintains WebSocket connection across page navigation
 */
export const GameWebSocketProvider = ({ children }) => {
  const socketRef = useRef(null);
  const gameTokenRef = useRef(null);
  const [isConnected, setIsConnected] = useState(false);
  
  // Single game object containing all game data
  const [gameState, setGameState] = useState(null);
  // Structure: {
  //   gameCode: string,
  //   boardData: object,
  //   turnInfo: { currentPlayer, currentPlayerName, turnNumber },
  //   players: [{ playerId, playerName, isOnline, isReady }],
  //   playerPositions: { playerName: position },
  //   connectedPlayers: [],
  //   readyPlayers: [],
  //   state: string,
  //   isGamemaster: boolean
  // }
  
  const [error, setError] = useState(null);
  const [isGamemaster, setIsGamemaster] = useState(false);
  const [gameStarted, setGameStarted] = useState(false);
  const [pendingPlayers, setPendingPlayers] = useState([]);
  const [approvalStatus, setApprovalStatus] = useState(null);
  const [playerIdentifier, setPlayerIdentifier] = useState(null);
  const [isMyTurnFlag, setIsMyTurnFlag] = useState(false); // Directly controlled by game:your-turn
  const [playerDiceRolls, setPlayerDiceRolls] = useState({});

  // Memoized derived values - extract from single game object
  const players = useMemo(() => {
    return gameState?.players || [];
  }, [gameState?.players]);

  const playerPositions = useMemo(() => {
    return gameState?.playerPositions || {};
  }, [gameState?.playerPositions]);

  const boardData = useMemo(() => {
    return gameState?.boardData || null;
  }, [gameState?.boardData]);

  const currentTurn = useMemo(() => gameState?.turnInfo?.currentPlayer || null, [gameState?.turnInfo?.currentPlayer]);
  const currentTurnName = useMemo(() => gameState?.turnInfo?.currentPlayerName || null, [gameState?.turnInfo?.currentPlayerName]);

  // isMyTurn is simply the flag set by game:your-turn event
  const isMyTurn = isMyTurnFlag;

  /**
   * Connect to game WebSocket with a game token
   * This maintains the connection even when navigating between pages
   */
  const connect = useCallback((gameToken) => {
    if (!gameToken) {
      warn('⚠️ Cannot connect without game token');
      return;
    }

    // If already connected with same token, don't reconnect
    if (socketRef.current?.connected && gameTokenRef.current === gameToken) {
      log('✅ Already connected with same token');
      return;
    }

    // Disconnect old socket if exists
    if (socketRef.current) {
      log('🔌 Disconnecting old socket');
      socketRef.current.removeAllListeners();
      socketRef.current.disconnect();
    }

    log('🔌 Connecting to game WebSocket...');
    gameTokenRef.current = gameToken;

    // Decode token to get player identifier
    try {
      const payload = JSON.parse(atob(gameToken.split('.')[1]));
      const identifier = payload.userId || payload.playerName;
      setPlayerIdentifier(identifier);
      log('🎮 Player identifier:', identifier);
    } catch (err) {
      logError('Failed to decode game token:', err);
    }

    // Connect to /game namespace
    socketRef.current = io(`${API_CONFIG.wsURL}/game`, {
      transports: ['websocket'],
      reconnection: true,
      reconnectionAttempts: 5,
      reconnectionDelay: 1000,
      timeout: 5000,
    });

    const socket = socketRef.current;

    // Connection handlers
    socket.on('connect', () => {
      log('✅ Connected to game WebSocket');
      setIsConnected(true);
      setError(null);
      socket.emit('game:join', { gameToken });
    });

    socket.on('connect_error', (err) => {
      logError('❌ Connection error:', err);
      setIsConnected(false);
      setError(err.message);
      
      // If reconnection fails completely, navigate to home
      if (err.message?.includes('timeout') || err.message?.includes('xhr poll error')) {
        setTimeout(() => {
          if (!socketRef.current?.connected) {
            logError('⚠️ Connection failed - redirecting to home');
            window.location.href = '/';
          }
        }, 10000); // Give 10 seconds for reconnection attempts
      }
    });

    socket.on('disconnect', (reason) => {
      log('🔌 Disconnected:', reason);
      setIsConnected(false);
      setIsMyTurnFlag(false); // Clear turn flag on disconnect
    });

    // Game state handlers
    socket.on('game:state', (state) => {
      log('📊 Game state received:', state);
      if (state?.isGamemaster !== undefined) {
        setIsGamemaster(state.isGamemaster);
      }
      // Merge state into single game object
      setGameState(prev => {
        // Build players array from various sources
        let playersArray = prev?.players || [];
        
        // If we have currentPlayers or players from backend, use them (already have proper structure)
        if (state.currentPlayers && Array.isArray(state.currentPlayers) && state.currentPlayers.length > 0) {
          playersArray = state.currentPlayers.map(p => ({
            playerId: p.playerId || p.id,
            playerName: p.playerName || p.name,
            name: p.playerName || p.name, // Add name for compatibility
            isOnline: p.isOnline !== undefined ? p.isOnline : true,
            isReady: p.isReady || false
          }));
        } else if (state.players && Array.isArray(state.players) && state.players.length > 0) {
          playersArray = state.players.map(p => ({
            playerId: p.playerId || p.id,
            playerName: p.playerName || p.name,
            name: p.playerName || p.name, // Add name for compatibility
            isOnline: p.isOnline !== undefined ? p.isOnline : true,
            isReady: p.isReady || false
          }));
        }
        // If players array is still empty but we have connectedPlayers (array of strings), convert them
        else if (playersArray.length === 0 && state.connectedPlayers && Array.isArray(state.connectedPlayers) && state.connectedPlayers.length > 0) {
          playersArray = state.connectedPlayers.map((playerName, index) => ({
            playerId: `player-${index}`, // Temporary ID until we get the real one
            playerName: playerName,
            name: playerName, // Add name for compatibility
            isOnline: true,
            isReady: false
          }));
        }
        
        // Initialize playerPositions from backend data if joining game in progress
        let playerPositions = prev?.playerPositions || {};
        // If we don't have positions yet but backend sent players with positions, initialize from them
        if (Object.keys(playerPositions).length === 0 && state.players && Array.isArray(state.players)) {
          const positionsFromBackend = {};
          state.players.forEach(p => {
            const playerName = p.playerName || p.name;
            if (playerName && p.position !== undefined) {
              positionsFromBackend[playerName] = p.position;
            }
          });
          if (Object.keys(positionsFromBackend).length > 0) {
            playerPositions = positionsFromBackend;
          }
        }
        
        const merged = {
          ...prev,
          gameCode: state.gameCode || prev?.gameCode,
          boardData: state.boardData || prev?.boardData,
          state: state.state || prev?.state,
          connectedPlayers: state.connectedPlayers || prev?.connectedPlayers || [],
          readyPlayers: state.readyPlayers || prev?.readyPlayers || [],
          // Preserve turn info - only turn-changed updates it
          turnInfo: prev?.turnInfo || { currentPlayer: null, currentPlayerName: null, turnNumber: 0 },
          // Use the built players array
          players: playersArray,
          // Initialize playerPositions from backend if joining in progress, otherwise preserve
          playerPositions: playerPositions
        };
        return merged;
      });
    });

    socket.on('game:state-update', (state) => {
      log('📊 Game state update:', state);
      if (state?.isGamemaster !== undefined) {
        setIsGamemaster(state.isGamemaster);
      }
      // Merge state update into single game object
      setGameState(prev => ({
        ...prev,
        gameCode: state.gameCode || prev?.gameCode,
        boardData: state.boardData || prev?.boardData,
        state: state.state || prev?.state,
        connectedPlayers: state.connectedPlayers || prev?.connectedPlayers,
        readyPlayers: state.readyPlayers || prev?.readyPlayers,
        // Preserve turn info and positions
        turnInfo: prev?.turnInfo,
        players: prev?.players,
        playerPositions: prev?.playerPositions
      }));
    });

    socket.on('game:joined', (data) => {
      log('✅ Joined game:', data);
      if (data.isGamemaster !== undefined) {
        setIsGamemaster(data.isGamemaster);
      }
      // Initialize or update gameState with gameCode and gameId from join confirmation
      setGameState(prev => {
        if (prev && prev.gameCode) {
          // If already has gameCode, just add gameId if missing
          return {
            ...prev,
            gameId: data.gameId || prev.gameId,
            gameCode: data.gameCode,
            playerName: data.playerName,
            isAuthenticated: data.isAuthenticated
          };
        }
        return {
          ...prev,
          gameId: data.gameId, // Store gameId from backend
          gameCode: data.gameCode,
          playerName: data.playerName,
          isAuthenticated: data.isAuthenticated
        };
      });
    });

    socket.on('game:player-joined', (data) => {
      log('👤 Player joined:', data.playerName);
      setGameState(prev => {
        if (!prev) return prev;
        const currentConnected = prev.connectedPlayers || [];
        const currentPlayers = prev.players || [];
        
        // Add to connectedPlayers if not already there
        const updatedConnected = currentConnected.includes(data.playerName) 
          ? currentConnected 
          : [...currentConnected, data.playerName];
        
        // Add to players array if not already there (without position - that's in playerPositions)
        const playerExists = currentPlayers.some(p => 
          p.playerName === data.playerName || p.name === data.playerName
        );
        
        const updatedPlayers = playerExists 
          ? currentPlayers 
          : [...currentPlayers, {
              playerId: data.playerId,
              playerName: data.playerName,
              name: data.playerName, // Add name for compatibility with Lobby display
              isOnline: true,
              isReady: false
            }];
        
        return {
          ...prev,
          connectedPlayers: updatedConnected,
          players: updatedPlayers
        };
      });
      window.dispatchEvent(new CustomEvent('game:player-joined', { detail: data }));
    });

    socket.on('game:player-left', (data) => {
      log('👋 Player left:', data.playerName);
      setGameState(prev => {
        if (!prev) return prev;
        return {
          ...prev,
          connectedPlayers: (prev.connectedPlayers || []).filter(p => p !== data.playerName)
        };
      });
      window.dispatchEvent(new CustomEvent('game:player-left', { detail: data }));
    });

    socket.on('game:player-ready', (data) => {
      log('✅ Player ready:', data.playerName);
      setGameState(prev => {
        if (!prev) return prev;
        const readyPlayers = prev.readyPlayers || [];
        if (data.ready && !readyPlayers.includes(data.playerName)) {
          return { ...prev, readyPlayers: [...readyPlayers, data.playerName] };
        } else if (!data.ready) {
          return { ...prev, readyPlayers: readyPlayers.filter(p => p !== data.playerName) };
        }
        return prev;
      });
      window.dispatchEvent(new CustomEvent('game:player-ready', { detail: data }));
    });

    socket.on('game:all-ready', (data) => {
      log('✅ All players ready! Game can start.');
      window.dispatchEvent(new CustomEvent('game:all-ready', { detail: data }));
    });

    socket.on('game:start', (data) => {
      log('🎮 Game started:', data);
      setGameStarted(true);
      
      // Update game state with position initialization
      setGameState(prev => {
        // Initialize all player positions to 0 at game start
        const initialPositions = {};
        
        // Use existing players from prev (already set by game:joined/game:state)
        const existingPlayers = prev?.players || [];
        
        // Initialize positions for all existing players
        existingPlayers.forEach((player) => {
          const playerName = player.playerName || player.name;
          if (playerName) {
            // Initialize ALL positions to 0 - only game:player-arrived will change them
            initialPositions[playerName] = 0;
          }
        });
        
        const updated = {
          ...prev,
          gameCode: data.gameCode || prev?.gameCode,
          boardData: data.boardData || prev?.boardData,
          state: data.gamePhase || data.state || 'playing',
          boardSize: data.boardSize || prev?.boardSize,
          // Keep existing players list, initialize positions to 0
          players: existingPlayers,
          playerPositions: initialPositions,
          // Preserve turn info and other data
          turnInfo: prev?.turnInfo || { currentPlayer: null, currentPlayerName: null, turnNumber: 0 },
          connectedPlayers: prev?.connectedPlayers || [],
          readyPlayers: prev?.readyPlayers || []
        };
        return updated;
      });
    });

    socket.on('game:turn-changed', (data) => {
      log('🔄 Turn changed to:', data.currentPlayerName);
      
      // Turn changed means it's NOT my turn anymore
      setIsMyTurnFlag(false);
      
      // This is the ONLY place turnInfo should be set
      setGameState(prev => ({
        ...prev,
        turnInfo: {
          currentPlayer: data.currentPlayer,
          currentPlayerName: data.currentPlayerName,
          turnNumber: data.turnNumber || prev?.turnInfo?.turnNumber || 0
        }
      }));
      
      // Force a re-render by logging after state update
      setTimeout(() => {
        console.log('🔄 [game:turn-changed] State should be updated now');
      }, 100);
    });

    socket.on('game:error', (err) => {
      logError('❌ Game error:', err);
      setError(err.message);
    });

    // Approval system handlers
    socket.on('game:pending-approval', (data) => {
      log('⏳ Pending gamemaster approval:', data);
      setApprovalStatus('pending');
      setError('Waiting for gamemaster approval...');
    });

    socket.on('game:player-requesting-join', (data) => {
      log('🔔 Player requesting to join:', data.playerName);
      setPendingPlayers(prev => {
        if (prev.some(p => p.playerName === data.playerName)) return prev;
        return [...prev, {
          playerName: data.playerName,
          isAuthenticated: data.isAuthenticated,
          timestamp: data.timestamp
        }];
      });
    });

    socket.on('game:approval-granted', (data) => {
      log('✅ Join request approved:', data);
      setApprovalStatus('approved');
      setError(null);
      socket.emit('game:join-approved', { gameToken });
    });

    socket.on('game:approval-denied', (data) => {
      logError('❌ Join request denied:', data.reason || data.message);
      setApprovalStatus('denied');
      setError(data.reason || 'Your request to join was denied');
    });

    socket.on('game:player-approved', (data) => {
      log('✅ Player approved by gamemaster:', data.playerName);
      setPendingPlayers(prev => prev.filter(p => p.playerName !== data.playerName));
      setGameState(prev => {
        if (!prev) return prev;
        const currentConnected = prev.connectedPlayers || [];
        if (!currentConnected.includes(data.playerName)) {
          return {
            ...prev,
            connectedPlayers: [...currentConnected, data.playerName]
          };
        }
        return prev;
      });
    });

    socket.on('game:your-turn', (data) => {
      log('🎯 Your turn!', data);
      console.log('🎯 [game:your-turn] Received - enabling dice');
      
      // Set flag to true - dice is now enabled
      setIsMyTurnFlag(true);
      
      // Emit custom event for GameScreen to display turn notification
      window.dispatchEvent(new CustomEvent('game:your-turn', { detail: data }));
    });

    socket.on('game:dice-rolled', (data) => {
      log('🎲 Dice rolled:', data.diceValue, 'by', data.playerName);
      // Track the dice roll for this player
      setPlayerDiceRolls(prev => ({
        ...prev,
        [data.playerName]: data.diceValue
      }));
      window.dispatchEvent(new CustomEvent('game:dice-rolled', { detail: data }));
    });

    socket.on('game:player-moving', (data) => {
      log('🚶 Player moving:', data.playerName, 'from', data.fromPosition, 'to', data.toPosition);
      window.dispatchEvent(new CustomEvent('game:player-moving', { detail: data }));
    });

    socket.on('game:player-arrived', (data) => {
      log('🎯 Player arrived:', data.playerName, 'at position', data.position, '(' + data.fieldType + ')');
      
      // Update playerPositions in game object - THIS IS THE ONLY PLACE POSITIONS ARE MODIFIED
      setGameState(prev => {
        if (!prev) return prev;
        const updatedPositions = { ...prev.playerPositions, [data.playerName]: data.position };
        
        return {
          ...prev,
          playerPositions: updatedPositions
        };
      });
      
      window.dispatchEvent(new CustomEvent('game:player-arrived', { detail: data }));
    });

    socket.on('game:card-drawn', (data) => {
      log('🃏 Card drawn:', data.cardType, 'by', data.playerName);
      window.dispatchEvent(new CustomEvent('game:card-drawn', { detail: data }));
    });

    socket.on('game:card-result', (data) => {
      log('🎴 Card result:', data.playerName, data.description);
      window.dispatchEvent(new CustomEvent('game:card-result', { detail: data }));
    });

    socket.on('game:answer-timeout', (data) => {
      log('⏰ Answer timeout:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:answer-timeout', { detail: data }));
    });

    socket.on('game:answer-result', (data) => {
      log('📝 Answer result:', data.correct ? '✅ Correct' : '❌ Wrong', '-', data.playerName);
      window.dispatchEvent(new CustomEvent('game:answer-result', { detail: data }));
    });

    socket.on('game:gamemaster-decision-request', (data) => {
      log('👨‍⚖️ Gamemaster decision request:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:gamemaster-decision-request', { detail: data }));
    });

    socket.on('game:gamemaster-decision-result', (data) => {
      log('⚖️ Gamemaster decision result:', data.decision, '-', data.playerName);
      window.dispatchEvent(new CustomEvent('game:gamemaster-decision-result', { detail: data }));
    });

    socket.on('game:card-drawn-self', (data) => {
      log('🃏 You drew a card:', data.cardType);
      window.dispatchEvent(new CustomEvent('game:card-drawn-self', { detail: data }));
    });

    socket.on('game:joker-activated', (data) => {
      log('🃏 Joker activated:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:joker-activated', { detail: data }));
    });

    socket.on('game:extra-turn', (data) => {
      log('⭐ Extra turn:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:extra-turn', { detail: data }));
    });

    socket.on('game:turn-lost', (data) => {
      log('😞 Turn lost:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:turn-lost', { detail: data }));
    });

    socket.on('game:no-movement', (data) => {
      log('⛔ No movement:', data.playerName, '-', data.reason);
      window.dispatchEvent(new CustomEvent('game:no-movement', { detail: data }));
    });

    socket.on('game:penalty-avoided', (data) => {
      log('🛡️ Penalty avoided:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:penalty-avoided', { detail: data }));
    });

    socket.on('game:guess-timeout', (data) => {
      log('⏰ Guess timeout:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:guess-timeout', { detail: data }));
    });

    socket.on('game:player-guessing', (data) => {
      log('🤔 Player guessing:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:player-guessing', { detail: data }));
    });

    socket.on('game:secondary-landing', (data) => {
      log('🎯 Secondary landing:', data.playerName, 'on', data.fieldType);
      window.dispatchEvent(new CustomEvent('game:secondary-landing', { detail: data }));
    });

    socket.on('game:player-disconnected', (data) => {
      log('⚠️ Player disconnected:', data.playerName);
      setGameState(prev => {
        if (!prev) return prev;
        return {
          ...prev,
          connectedPlayers: (prev.connectedPlayers || []).filter(p => p !== data.playerName)
        };
      });
      window.dispatchEvent(new CustomEvent('game:player-disconnected', { detail: data }));
    });

    socket.on('game:player-disconnected-during-turn', (data) => {
      log('⚠️ Player disconnected during turn:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:player-disconnected-during-turn', { detail: data }));
    });

    socket.on('game:player-disconnected-during-card', (data) => {
      log('⚠️ Player disconnected during card:', data.playerName);
      window.dispatchEvent(new CustomEvent('game:player-disconnected-during-card', { detail: data }));
    });

    socket.on('game:guess-result', (data) => {
      log('🎯 Guess result:', data);
      // Position update will come from game:player-arrived event
      window.dispatchEvent(new CustomEvent('game:guess-result', { detail: data }));
    });

    socket.on('game:joker-complete', (data) => {
      log('🃏 Joker complete:', data);
      // Position update will come from game:player-arrived event
      window.dispatchEvent(new CustomEvent('game:joker-complete', { detail: data }));
    });

    socket.on('game:luck-consequence', (data) => {
      log('🍀 Luck consequence:', data);
      // Position update will come from game:player-arrived event
      window.dispatchEvent(new CustomEvent('game:luck-consequence', { detail: data }));
    });

    socket.on('game:ended', (data) => {
      log('🏁 Game ended! Winner:', data.winner);
      setGameState(prev => ({
        ...prev,
        status: 'finished',
        winner: data.winner,
        finalScores: data.scores
      }));
      window.dispatchEvent(new CustomEvent('game:ended', { detail: data }));
      
      // Don't auto-navigate - let the player close the winner modal manually
      // They can use the "Vissza a főoldalra" button when ready
    });

    socket.on('game:extra-turn-remaining', (data) => {
      log('⭐ Extra turn remaining:', data);
      window.dispatchEvent(new CustomEvent('game:extra-turn-remaining', { detail: data }));
    });

    socket.on('game:players-skipped', (data) => {
      log('⏭️ Players skipped:', data.skippedPlayers);
      window.dispatchEvent(new CustomEvent('game:players-skipped', { detail: data }));
    });

    socket.on('game:cleanup-complete', (data) => {
      log('🧹 Cleanup complete:', data);
      window.dispatchEvent(new CustomEvent('game:cleanup-complete', { detail: data }));
      
      // Navigate to home after cleanup
      setTimeout(() => {
        log('🏠 Navigating to home after cleanup');
        window.location.href = '/';
      }, 2000);
    });
  }, []);

  /**
   * Disconnect from WebSocket
   */
  const disconnect = useCallback(() => {
    if (socketRef.current) {
      log('🔌 Disconnecting from game WebSocket');
      socketRef.current.removeAllListeners();
      socketRef.current.disconnect();
      socketRef.current = null;
      gameTokenRef.current = null;
      setIsConnected(false);
      setGameState(null); // Clear entire game object
      setError(null);
      setIsGamemaster(false);
      setGameStarted(false);
      setPendingPlayers([]);
      setApprovalStatus(null);
    }
  }, []);

  // Action methods
  const rollDice = useCallback((diceValue) => {
    const socket = socketRef.current;
    if (!socket || !isConnected) {
      warn('⚠️ Cannot roll dice: not connected');
      return false;
    }
    log('🎲 Rolling dice:', diceValue);
    socket.emit('game:dice-roll', { gameCode: gameState?.gameCode, diceValue });
    return true;
  }, [isConnected, gameState?.gameCode]);

  const sendMessage = useCallback((message) => {
    const socket = socketRef.current;
    if (!socket || !isConnected) return false;
    socket.emit('game:chat', { gameCode: gameState?.gameCode, message });
    return true;
  }, [isConnected, gameState?.gameCode]);

  const setReady = useCallback((ready = true) => {
    const socket = socketRef.current;
    if (!socket || !isConnected) return false;
    socket.emit('game:ready', { gameCode: gameState?.gameCode, ready });
    return true;
  }, [isConnected, gameState?.gameCode]);

  const leaveGame = useCallback(() => {
    const socket = socketRef.current;
    if (!socket || !isConnected) return false;
    socket.emit('game:leave', { gameCode: gameState?.gameCode });
    return true;
  }, [isConnected, gameState?.gameCode]);

  const approvePlayer = useCallback((playerName) => {
    const socket = socketRef.current;
    if (!socket || !isConnected || !isGamemaster) return false;
    socket.emit('game:approve-player', { gameCode: gameState?.gameCode, playerName });
    return true;
  }, [isConnected, isGamemaster, gameState?.gameCode]);

  const rejectPlayer = useCallback((playerName, reason = 'Join request denied') => {
    const socket = socketRef.current;
    if (!socket || !isConnected || !isGamemaster) return false;
    socket.emit('game:reject-player', { gameCode: gameState?.gameCode, playerName, reason });
    return true;
  }, [isConnected, isGamemaster, gameState?.gameCode]);

  const submitAnswer = useCallback((answer, cardId = null) => {
    const socket = socketRef.current;
    if (!socket || !isConnected) {
      warn('⚠️ Cannot submit answer: not connected');
      return false;
    }
    log('📝 Submitting answer:', answer, 'for card:', cardId);
    socket.emit('game:card-answer', { 
      gameCode: gameState?.gameCode, 
      answer,
      cardId 
    });
    return true;
  }, [isConnected, gameState?.gameCode]);

  const submitPositionGuess = useCallback((guessedPosition) => {
    const socket = socketRef.current;
    if (!socket || !isConnected) {
      warn('⚠️ Cannot submit position guess: not connected');
      return false;
    }
    log('🎯 Submitting position guess:', guessedPosition);
    socket.emit('game:position-guess', { gameCode: gameState?.gameCode, guessedPosition });
    return true;
  }, [isConnected, gameState?.gameCode]);

  const submitJokerPositionGuess = useCallback((guessedPosition) => {
    const socket = socketRef.current;
    if (!socket || !isConnected) {
      warn('⚠️ Cannot submit joker position guess: not connected');
      return false;
    }
    log('🃏🎯 Submitting joker position guess:', guessedPosition);
    socket.emit('game:joker-position-guess', { gameCode: gameState?.gameCode, guessedPosition });
    return true;
  }, [isConnected, gameState?.gameCode]);

  const approveJoker = useCallback((requestId) => {
    const socket = socketRef.current;
    if (!socket || !isConnected || !isGamemaster) {
      warn('⚠️ Cannot approve joker: not gamemaster or not connected');
      return false;
    }
    log('✅ Approving joker request:', requestId);
    socket.emit('game:gamemaster-decision', { 
      gameCode: gameState?.gameCode, 
      requestId, 
      decision: 'approve' 
    });
    return true;
  }, [isConnected, isGamemaster, gameState?.gameCode]);

  const rejectJoker = useCallback((requestId, reason = 'Joker answer rejected') => {
    const socket = socketRef.current;
    if (!socket || !isConnected || !isGamemaster) {
      warn('⚠️ Cannot reject joker: not gamemaster or not connected');
      return false;
    }
    log('❌ Rejecting joker request:', requestId, 'Reason:', reason);
    socket.emit('game:gamemaster-decision', { 
      gameCode: gameState?.gameCode, 
      requestId, 
      decision: 'reject',
      reason 
    });
    return true;
  }, [isConnected, isGamemaster, gameState?.gameCode]);

  const addEventListener = useCallback((event, handler) => {
    const socket = socketRef.current;
    if (socket) {
      socket.on(event, handler);
    }
  }, []);

  const removeEventListener = useCallback((event, handler) => {
    const socket = socketRef.current;
    if (socket) socket.off(event, handler);
  }, []);

  const value = {
    socket: socketRef.current,
    isConnected,
    gameState, // Single game object with all data
    players, // Memoized from gameState.players
    playerPositions, // Memoized from gameState.playerPositions (SINGLE SOURCE OF TRUTH for positions)
    boardData, // Memoized from gameState.boardData
    currentTurn, // Memoized from gameState.turnInfo.currentPlayer
    currentTurnName, // Memoized from gameState.turnInfo.currentPlayerName
    isMyTurn,
    playerIdentifier,
    error,
    isGamemaster,
    gameStarted,
    pendingPlayers,
    approvalStatus,
    playerDiceRolls,
    // Connection management
    connect,
    disconnect,
    // Methods
    rollDice,
    sendMessage,
    setReady,
    leaveGame,
    approvePlayer,
    rejectPlayer,
    submitAnswer,
    submitPositionGuess,
    submitJokerPositionGuess,
    approveJoker,
    rejectJoker,
    addEventListener,
    removeEventListener,
  };

  return (
    <GameWebSocketContext.Provider value={value}>
      {children}
    </GameWebSocketContext.Provider>
  );
};

/**
 * Hook to access the shared WebSocket connection
 */
export const useGameWebSocketContext = () => {
  const context = useContext(GameWebSocketContext);
  if (!context) {
    throw new Error('useGameWebSocketContext must be used within GameWebSocketProvider');
  }
  return context;
};
