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

class SocketHandlers {
  constructor(io) {
    this.io = io;
    this.connectedUsers = new Map(); // userId -> socket.id
    this.userSockets = new Map(); // socket.id -> userId
  }

  handleConnection(socket) {
    logger.info(`Client connected: ${socket.id}`);

    // Authentication
    socket.on('authenticate', async (token) => {
      try {
        const jwt = require('jsonwebtoken');
        const decoded = jwt.verify(token, process.env.JWT_SECRET || 'fallback_secret');
        
        const player = await Player.findOne({ userId: decoded.userId });
        if (!player) {
          socket.emit('auth_error', { error: 'Player not found' });
          return;
        }

        // Store user connection
        this.connectedUsers.set(decoded.userId, socket.id);
        this.userSockets.set(socket.id, decoded.userId);
        
        socket.userId = decoded.userId;
        socket.emit('authenticated', { userId: decoded.userId });
        
        logger.info(`User authenticated: ${decoded.userId}`);
        
        // Join user to their current server if any
        if (player.currentServer) {
          socket.join(player.currentServer);
          this.broadcastToServer(player.currentServer, 'user_joined', {
            userId: decoded.userId,
            username: player.username
          });
        }
        
      } catch (error) {
        logger.error('Authentication error:', error);
        socket.emit('auth_error', { error: 'Invalid token' });
      }
    });

    // Server management
    socket.on('join_server', async (data) => {
      try {
        if (!socket.userId) {
          socket.emit('error', { error: 'Not authenticated' });
          return;
        }

        const gameSystem = getGameSystem();
        const server = await gameSystem.joinServer(data.serverId, socket.userId);
        
        // Update player's current server
        await Player.findOneAndUpdate(
          { userId: socket.userId },
          { currentServer: data.serverId }
        );
        
        // Join socket room
        socket.join(data.serverId);
        
        socket.emit('server_joined', { server });
        this.broadcastToServer(data.serverId, 'user_joined', {
          userId: socket.userId,
          serverId: data.serverId
        });
        
        logger.info(`User ${socket.userId} joined server ${data.serverId}`);
        
      } catch (error) {
        logger.error('Error joining server:', error);
        socket.emit('error', { error: error.message });
      }
    });

    socket.on('leave_server', async (data) => {
      try {
        if (!socket.userId) {
          socket.emit('error', { error: 'Not authenticated' });
          return;
        }

        const gameSystem = getGameSystem();
        const server = await gameSystem.leaveServer(data.serverId, socket.userId);
        
        // Update player's current server
        await Player.findOneAndUpdate(
          { userId: socket.userId },
          { currentServer: null }
        );
        
        // Leave socket room
        socket.leave(data.serverId);
        
        socket.emit('server_left', { server });
        this.broadcastToServer(data.serverId, 'user_left', {
          userId: socket.userId,
          serverId: data.serverId
        });
        
        logger.info(`User ${socket.userId} left server ${data.serverId}`);
        
      } catch (error) {
        logger.error('Error leaving server:', error);
        socket.emit('error', { error: error.message });
      }
    });

    // Game actions
    socket.on('game_action', async (data) => {
      try {
        if (!socket.userId) {
          socket.emit('error', { error: 'Not authenticated' });
          return;
        }

        const gameSystem = getGameSystem();
        const result = await gameSystem.processGameAction(socket.userId, data);
        
        socket.emit('action_result', { action: data.type, result });
        
        // Broadcast relevant actions to server
        if (data.broadcast && socket.userId) {
          const player = await Player.findOne({ userId: socket.userId });
          if (player && player.currentServer) {
            this.broadcastToServer(player.currentServer, 'user_action', {
              userId: socket.userId,
              username: player.username,
              action: data.type,
              result
            });
          }
        }
        
      } catch (error) {
        logger.error('Error processing game action:', error);
        socket.emit('error', { error: error.message });
      }
    });

    // Chat functionality
    socket.on('send_message', async (data) => {
      try {
        if (!socket.userId) {
          socket.emit('error', { error: 'Not authenticated' });
          return;
        }

        const player = await Player.findOne({ userId: socket.userId });
        if (!player || !player.currentServer) {
          socket.emit('error', { error: 'Not in a server' });
          return;
        }

        const message = {
          userId: socket.userId,
          username: player.username,
          message: data.message,
          timestamp: new Date(),
          type: data.type || 'chat'
        };

        // Broadcast to server
        this.broadcastToServer(player.currentServer, 'new_message', message);
        
        logger.info(`Chat message from ${socket.userId} in server ${player.currentServer}`);
        
      } catch (error) {
        logger.error('Error sending message:', error);
        socket.emit('error', { error: error.message });
      }
    });

    // Real-time updates
    socket.on('request_server_status', async () => {
      try {
        if (!socket.userId) {
          socket.emit('error', { error: 'Not authenticated' });
          return;
        }

        const player = await Player.findOne({ userId: socket.userId });
        if (!player || !player.currentServer) {
          socket.emit('server_status', { server: null });
          return;
        }

        const gameSystem = getGameSystem();
        const server = gameSystem.servers.get(player.currentServer);
        
        if (server) {
          const players = await Player.find({ 
            userId: { $in: server.players } 
          }).select('userId username info.stats.level');

          socket.emit('server_status', {
            server: {
              id: server.id,
              name: server.name,
              currentPlayers: server.players.length,
              maxPlayers: server.maxPlayers,
              players: players.map(p => ({
                userId: p.userId,
                username: p.username,
                level: p.info.stats.level
              }))
            }
          });
        }
        
      } catch (error) {
        logger.error('Error getting server status:', error);
        socket.emit('error', { error: error.message });
      }
    });

    // Disconnection
    socket.on('disconnect', async () => {
      logger.info(`Client disconnected: ${socket.id}`);
      
      const userId = this.userSockets.get(socket.id);
      if (userId) {
        // Remove from tracking
        this.connectedUsers.delete(userId);
        this.userSockets.delete(socket.id);
        
        // Notify server if user was in one
        const player = await Player.findOne({ userId });
        if (player && player.currentServer) {
          this.broadcastToServer(player.currentServer, 'user_disconnected', {
            userId,
            username: player.username
          });
        }
      }
    });
  }

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

  sendToUser(userId, event, data) {
    const socketId = this.connectedUsers.get(userId);
    if (socketId) {
      this.io.to(socketId).emit(event, data);
    }
  }

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

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

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

module.exports = SocketHandlers;
