const WebSocket = require('ws');
const jwt = require('jsonwebtoken');

// user_id -> websocket mapping
const userSocketMap = new Map();

// Keep track of connection attempts to prevent rapid reconnection loops
const connectionAttempts = new WeakMap();

/**
 * Initializes the WebSocket server.
 * @param {https.Server} server - The HTTP/HTTPS server instance from Express.
 */
function initWebSocket(server) {
  const wss = new WebSocket.Server({ 
    server,
    clientTracking: true,
    // Consider adding ping/pong for connection health
    // clientTracking: true,
    // maxPayload: 100 * 1024 * 1024, // 100MB
  });

  console.log('WebSocket server initialized');

  // Handle new connections
  wss.on('connection', (ws, req) => {
    const clientIp = req.socket.remoteAddress;
    // console.log(`New WebSocket connection from ${clientIp}`);
    
    // Set up ping/pong to detect dead connections
    let isAlive = true;
    ws.isAlive = true;
    
    const pingInterval = setInterval(() => {
      if (!isAlive) {
        console.log('Terminating dead connection');
        return ws.terminate();
      }
      
      isAlive = false;
      try {
        ws.ping(() => {});
      } catch (e) {
        console.error('Error sending ping:', e);
      }
    }, 30000);
    
    ws.on('pong', () => {
      isAlive = true;
    });

    ws.on('message', (message) => {
      try {
        const data = JSON.parse(message);
        
        // Handle different message types
        if (data.type === 'MESSAGE') {
          console.log(`Received message: ${data.content}`);
          ws.send(JSON.stringify({ 
            type: 'MESSAGE_RECEIVED', 
            content: data.content,
            timestamp: new Date().toISOString()
          }));
          return;
        }
        
        // Handle authentication with token
        const { token } = data;

        if (!token) {
          ws.send(JSON.stringify({ 
            type: 'ERROR',
            error: 'No token provided',
            timestamp: new Date().toISOString()
          }));
          return;
        }

        const decoded = jwt.decode(token); // Use jwt.verify() in production
        const userId = decoded?.claims?.user_id;

        if (!userId) {
          ws.send(JSON.stringify({ 
            type: 'ERROR',
            error: 'Invalid token or missing user_id in claims',
            timestamp: new Date().toISOString()
          }));
          return;
        }

        console.log(`Authenticated user: ${userId}`);
        
        // Store the WebSocket connection with the user ID
        userSocketMap.set(userId, ws);
        
        // Store user ID in the WebSocket object for cleanup
        ws.userId = userId;

        ws.send(JSON.stringify({ 
          type: 'CONNECTED',
          message: `Connected as ${userId}`,
          timestamp: new Date().toISOString()
        }));

      } catch (err) {
        console.error('WebSocket message error:', err);
        try {
          ws.send(JSON.stringify({ 
            type: 'ERROR',
            error: 'Invalid message format or processing error',
            timestamp: new Date().toISOString()
          }));
        } catch (sendErr) {
          console.error('Failed to send error message:', sendErr);
        }
      }
    });

    // Handle connection close
    ws.on('close', () => {
      // console.log(`WebSocket closed for ${ws.userId || 'unknown user'}`);
      if (ws.userId) {
        userSocketMap.delete(ws.userId);
      }
      clearInterval(pingInterval);
    });
    
    // Handle errors
    ws.on('error', (error) => {
      console.error('WebSocket error:', error);
      if (ws.readyState === WebSocket.OPEN) {
        try {
          ws.close(1011, 'Internal server error');
        } catch (e) {
          // Ignore errors during close
        }
      }
    });
  });

  // Handle server errors
  wss.on('error', (error) => {
    console.error('WebSocket server error:', error);
  });

  return wss;
}

/**
 * Sends an alert to a user via WebSocket.
 * @param {string} userId - The target user ID.
 * @param {string|object} alert - The alert message or payload.
 * @returns {boolean} - Success/failure.
 */
function sendAlertToUser(userId, alert) {
  console.log("socket -- invoked---")
  const ws = userSocketMap.get(userId);
  if (ws && ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ALERT', payload: alert }));
    return true;
  }
  return false;
}

module.exports = {
  initWebSocket,
  sendAlertToUser,
};
