/**
 * Socket Handlers - Manages real-time socket connections for game server
 */

const jwt = require('jsonwebtoken');
const logger = require('../utils/logger');
const { getGameSystem } = require('../systems/GameSystem');

class SocketHandlers {
  constructor(io, gameServers, connectedPlayers) {
    this.io = io;
    this.gameServers = gameServers;
    this.connectedPlayers = connectedPlayers; // Track actual player connections
    this.gameSystem = null;
    
    // Track connected users to prevent duplicate connections
    this.connectedUsers = new Map(); // userId -> socket.id
    this.userSockets = new Map(); // socket.id -> userId
    
    // Add connection cleanup interval
    this.startConnectionCleanup();
  }
  
  startConnectionCleanup() {
    // Clean up stale connections every 30 seconds
    setInterval(() => {
      this.cleanupStaleConnections();
    }, 30000);
  }
  
  cleanupStaleConnections() {
    logger.info(`[GAME SERVER] Checking ${this.connectedUsers.size} active connections...`);
    logger.info(`[GAME SERVER] Current tracked players: ${Array.from(this.connectedPlayers)}`);
    
    let playersRemoved = 0;
    for (const [userId, socketId] of this.connectedUsers.entries()) {
      const socket = this.io.sockets.sockets.get(socketId);
      if (!socket || !socket.connected) {
        logger.warn(`[GAME SERVER] Cleaning up stale connection for user ${userId} (socket: ${socketId})`);
        this.connectedUsers.delete(userId);
        this.userSockets.delete(socketId);
        if (this.connectedPlayers.has(userId)) {
          this.connectedPlayers.delete(userId);
          playersRemoved++;
          logger.info(`[GAME SERVER] Removed stale player ${userId}. Players removed: ${playersRemoved}`);
        }
      }
    }
    
    logger.info(`[GAME SERVER] Cleanup complete. Players removed: ${playersRemoved}, Total players now: ${this.connectedPlayers.size}`);
    
    // Update player count on API if players were removed
    if (playersRemoved > 0 && this.serverRegistration) {
      logger.info(`[GAME SERVER] Updating API player count after cleanup to: ${this.connectedPlayers.size}`);
      this.serverRegistration.updatePlayerCount(this.connectedPlayers.size);
    }
  }
  
  async initializeGameSystem() {
    const { initializeGameSystems } = require('../systems/GameSystem');
    this.gameSystem = await initializeGameSystems();
  }

  handleConnection(socket) {
    logger.info(`Game Server: Socket connected - ${socket.id}`);

    // Authentication middleware
    socket.use(async (packet, next) => {
      try {
        const token = socket.handshake.auth.token;
        if (!token) {
          return next(new Error('Authentication required'));
        }

        // Verify JWT token
        const decoded = jwt.verify(token, process.env.JWT_SECRET || 'fallback_secret');
        socket.userId = decoded.userId;
        socket.email = decoded.email;
        
        // Check if user is already connected from another client
        const existingSocketId = this.connectedUsers.get(decoded.userId);
        const wasAlreadyConnected = this.connectedPlayers.has(decoded.userId);
        
        logger.info(`[GAME SERVER] User ${decoded.userId} (${decoded.email}) connecting. Was already connected: ${wasAlreadyConnected}, Existing socket: ${existingSocketId}`);
        
        if (existingSocketId && existingSocketId !== socket.id) {
          logger.warn(`[GAME SERVER] User ${decoded.userId} attempting to connect from multiple clients. Disconnecting previous client.`);
          logger.warn(`[GAME SERVER] Existing socket: ${existingSocketId}, New socket: ${socket.id}`);
          
          // Disconnect the previous client
          const previousSocket = this.io.sockets.sockets.get(existingSocketId);
          if (previousSocket) {
            logger.info(`[GAME SERVER] Force disconnecting previous socket: ${existingSocketId}`);
            previousSocket.emit('force_disconnect', { 
              reason: 'Another client connected with your account',
              newSocketId: socket.id 
            });
            previousSocket.disconnect(true);
          } else {
            logger.warn(`[GAME SERVER] Previous socket ${existingSocketId} not found in active connections`);
            // Clean up the stale mapping
            this.connectedUsers.delete(decoded.userId);
            this.userSockets.delete(existingSocketId);
            if (!wasAlreadyConnected) {
              this.connectedPlayers.delete(decoded.userId);
            }
          }
        } else {
          logger.info(`[GAME SERVER] New connection for user ${decoded.userId} (socket: ${socket.id})`);
        }
        
        // Store user connection
        this.connectedUsers.set(decoded.userId, socket.id);
        this.userSockets.set(socket.id, decoded.userId);
        
        // Add to connected players tracking only if not already there
        const wasNotAlreadyConnected = !this.connectedPlayers.has(decoded.userId);
        this.connectedPlayers.add(decoded.userId);
        
        logger.info(`[GAME SERVER] User ${decoded.userId} added to tracking. Was not already connected: ${wasNotAlreadyConnected}, Total players: ${this.connectedPlayers.size}`);
        
        // Update player count on API only if this is a new unique user
        if (this.serverRegistration && wasNotAlreadyConnected) {
          logger.info(`[GAME SERVER] Updating API player count to: ${this.connectedPlayers.size}`);
          this.serverRegistration.updatePlayerCount(this.connectedPlayers.size);
        }
        
        next();
      } catch (error) {
        logger.error(`Socket authentication failed: ${error.message}`);
        socket.emit('authError', { error: 'Authentication failed' });
        socket.disconnect();
      }
    });

    // Event handlers
    socket.on('joinServer', (data) => this.handleJoinServer(socket, data));
    socket.on('leaveServer', (data) => this.handleLeaveServer(socket, data));
    socket.on('gameAction', (data) => this.handleGameAction(socket, data));
    socket.on('chatMessage', (data) => this.handleChatMessage(socket, data));
    socket.on('getPlayerList', (data) => this.handleGetPlayerList(socket, data));
    socket.on('disconnect', () => this.handleDisconnect(socket));
  }

  async handleJoinServer(socket, data) {
    try {
      const { serverId, userId, username } = data;

      // Verify user matches socket authentication
      if (socket.userId !== userId) {
        socket.emit('error', { message: 'User authentication mismatch' });
        return;
      }

      // Create or get game instance
      let gameInstance = this.gameSystem.getGameInstance(serverId);
      if (!gameInstance) {
        // This should ideally be handled by the API server
        // But for now, create a basic game instance
        gameInstance = this.gameSystem.createGameInstance({
          id: serverId,
          name: `Game ${serverId}`,
          type: 'public',
          region: 'us-east',
          maxPlayers: 10
        });
      }

      // Join player to game instance
      const playerData = {
        userId: userId,
        username: username,
        currentShip: data.currentShip,
        stats: data.stats
      };

      const joinedGame = this.gameSystem.joinGameInstance(socket, serverId, playerData);

      if (joinedGame) {
        // Notify player of successful join
        socket.emit('joinedServer', {
          serverId: serverId,
          gameInstance: {
            id: joinedGame.id,
            name: joinedGame.name
          }
        });

        // Notify other players
        socket.to(`game_${gameId}`).emit('playerJoined', {
          userId: socket.userId,
          username: socket.email || 'Player'
        });

        logger.info(`Player ${socket.userId} joined game ${gameId}`);
      } else {
        socket.emit('error', { message: 'Failed to join game' });
      }
    } catch (error) {
      logger.error(`Join game error: ${error.message}`);
      socket.emit('error', { message: 'Failed to join game' });
    }
  }

  async handleLeaveGame(socket, data) {
    try {
      const { gameId } = data;

      const leftGame = this.gameSystem.leaveGameInstance(socket, gameId);

      if (leftGame) {
        // Notify player of successful leave
        socket.emit('leftGame', { gameId: gameId });

        // Notify other players
        socket.to(`game_${gameId}`).emit('playerLeft', {
          userId: socket.userId,
          currentPlayers: leftGame.currentPlayers
        });

        logger.info(`Player left game ${gameId}`);
      } else {
        socket.emit('error', { message: 'Failed to leave game' });
      }

    } catch (error) {
      logger.error(`Leave game error: ${error.message}`);
      socket.emit('error', { message: 'Failed to leave game' });
    }
  }

  async handleGameAction(socket, data) {
    try {
      const { type, actionData } = data;

      // Handle game action through game system
      const success = this.gameSystem.handlePlayerAction(socket, type, actionData);

      if (!success) {
        socket.emit('error', { message: 'Failed to process game action' });
      }

    } catch (error) {
      logger.error(`Game action error: ${error.message}`);
      socket.emit('error', { message: 'Failed to process game action' });
    }
  }

  async handleChatMessage(socket, data) {
    try {
      const { message } = data;

      // Handle chat through game system
      const success = this.gameSystem.handlePlayerChat(socket, { message });

      if (!success) {
        socket.emit('error', { message: 'Failed to send chat message' });
      }

    } catch (error) {
      logger.error(`Chat message error: ${error.message}`);
      socket.emit('error', { message: 'Failed to send chat message' });
    }
  }

  async handleGetPlayerList(socket, data) {
    try {
      const { serverId } = data;
      this.sendPlayerList(socket, serverId);
    } catch (error) {
      logger.error(`Get player list error: ${error.message}`);
      socket.emit('error', { message: 'Failed to get player list' });
    }
  }

  async sendPlayerList(socket, serverId) {
    const gameInstance = this.gameSystem.getGameInstance(serverId);
    
    if (gameInstance) {
      const players = Array.from(gameInstance.players.values()).map(player => ({
        userId: player.userId,
        username: player.username,
        joinedAt: player.joinedAt,
        isReady: player.isReady,
        stats: player.stats
      }));

      socket.emit('playerList', {
        serverId: serverId,
        players: players,
        currentPlayers: gameInstance.currentPlayers,
        maxPlayers: gameInstance.maxPlayers
      });
    } else {
      socket.emit('error', { message: 'Game instance not found' });
    }
  }

  async handleDisconnect(socket) {
    try {
      logger.info(`Game Server: Socket disconnected - ${socket.id}`);

      // Get user ID from socket
      const userId = this.userSockets.get(socket.id);
      if (userId) {
        logger.info(`[GAME SERVER] User ${userId} disconnecting (socket: ${socket.id})`);
        
        // Remove from tracking maps
        this.connectedUsers.delete(userId);
        this.userSockets.delete(socket.id);
        
        // Remove from connected players tracking
        const wasTracked = this.connectedPlayers.has(userId);
        this.connectedPlayers.delete(userId);
        
        logger.info(`[GAME SERVER] User ${userId} removed from tracking. Was tracked: ${wasTracked}, Total players: ${this.connectedPlayers.size}`);
        
        // Update player count on API only if user was being tracked
        if (this.serverRegistration && wasTracked) {
          logger.info(`[GAME SERVER] Updating API player count to: ${this.connectedPlayers.size}`);
          this.serverRegistration.updatePlayerCount(this.connectedPlayers.size);
        }
        
        // Get player connection info
        const connection = this.gameSystem.getPlayerConnection(socket.id);
        if (connection && connection.gameId) {
          const gameId = connection.gameId;
          
          // Remove player from game
          const success = this.gameSystem.removePlayerFromGame(gameId, userId);
          
          if (success) {
            // Notify other players in the game
            const game = this.gameSystem.getGame(gameId);
            if (game) {
              this.io.to(gameId).emit('playerLeft', {
                userId,
                username: socket.email || 'Unknown',
                gameId,
                currentPlayers: game.currentPlayers
              });
            }
            
            logger.info(`Player ${userId} disconnected from game ${gameId}`);
          }

        if (success) {
          // Notify other players
          this.io.to(`game_${gameId}`).emit('playerLeft', {
            userId: socket.userId,
            currentPlayers: game.currentPlayers
          });
          logger.info(`Player ${socket.userId} disconnected from game ${gameId}`);
        }
      }

      logger.info(`[GAME SERVER] User ${userId} fully disconnected and cleaned up`);
    } else {
      logger.warn(`[GAME SERVER] Unknown socket ${socket.id} disconnected without user mapping`);
    }
    } catch (error) {
      logger.error(`[GAME SERVER] Disconnect error: ${error.message}`);
    }
  }

  // Server management methods
  getServerStatus() {
    const gameInstances = this.gameSystem.getAllGameInstances();
    
    return {
      activeServers: gameInstances.length,
      connectedPlayers: this.connectedPlayers.size,
    };
  }

  getConnectedUsers() {
    return Array.from(this.connectedUsers.keys());
  }

  getUserCount() {
    return this.connectedUsers.size;
  }

  broadcastToAll(event, data) {
    this.io.emit(event, data);
  }

  broadcastToServer(serverId, event, data) {
    this.io.to(`game_${serverId}`).emit(event, data);
  }
}

module.exports = SocketHandlers;
