import { Server } from 'socket.io';

let io: Server | null = null;

// Track online users per request: { requestId: Set<userId> }
const onlineUsersPerRequest = new Map<string, Set<string>>();

export function initSocket(httpServer: any) {
  // Use only FRONTEND_URL from environment - no fallbacks
  const frontendUrl = process.env.FRONTEND_URL;
  const isProduction = process.env.NODE_ENV === 'production';
  
  let origins: string[];
  
  if (!frontendUrl) {
    if (isProduction) {
      console.error('❌ ERROR: FRONTEND_URL environment variable is not set for Socket.io!');
      console.error('   Socket.io will block all origins. This will prevent WebSocket connections.');
      origins = [];
    } else {
      console.warn('⚠️  Socket.io: FRONTEND_URL not set. Defaulting to http://localhost:3000 for development.');
      origins = ['http://localhost:3000'];
    }
  } else if (frontendUrl === '*') {
    if (isProduction) {
      console.warn('⚠️  WARNING: Socket.io FRONTEND_URL is set to allow ALL origins (*) in production. This is not recommended for security.');
    }
    origins = ['*'] as any; // Allow all origins
  } else {
    origins = frontendUrl.split(',').map(s => s.trim()).filter(Boolean);

    if (origins.length === 0) {
      console.error('❌ ERROR: FRONTEND_URL is set but contains no valid URLs for Socket.io!');
      origins = [];
    } else {
      // In development always allow localhost:3000 (Vite default) so WebSocket works when frontend is on 3000
      if (!isProduction && !origins.includes('http://localhost:3000')) {
        origins = ['http://localhost:3000', ...origins];
      }
      console.log(`✅ Socket.io: Allowing origins from FRONTEND_URL: ${origins.join(', ')}`);
    }
  }
  
  io = new Server(httpServer, {
    cors: {
      origin: origins.length === 1 && origins[0] === '*' ? true : origins,
      methods: ['GET', 'POST'],
      credentials: true
    },
    path: '/socket.io',
    transports: ['websocket', 'polling']
  });
  
  io.on('connection', (socket: any) => {
    let currentRequestId: string | null = null;
    let currentUserId: string | null = null;

    // Join user's personal notification room
    socket.on('join:user', (data: { userId: string }) => {
      const userId = typeof data === 'string' ? data : data.userId;
      socket.join(`user:${userId}`);
      currentUserId = userId;
    });

    socket.on('join:request', (data: { requestId: string; userId?: string }) => {
      const requestId = typeof data === 'string' ? data : data.requestId;
      const userId = typeof data === 'object' ? data.userId : null;
      
      socket.join(`request:${requestId}`);
      currentRequestId = requestId;
      currentUserId = userId || null;

      if (userId) {
        // Track this user as online for this request
        if (!onlineUsersPerRequest.has(requestId)) {
          onlineUsersPerRequest.set(requestId, new Set());
        }
        onlineUsersPerRequest.get(requestId)!.add(userId);

        const onlineUsers = Array.from(onlineUsersPerRequest.get(requestId) || []);

        // Broadcast presence to others in the room
        socket.to(`request:${requestId}`).emit('presence:join', { userId, requestId });
        
        // Send current online users to the newly joined socket
        socket.emit('presence:online', { requestId, userIds: onlineUsers });
      }
    });

    // Handle request for current online users (when component loads after join)
    socket.on('request:online-users', (data: { requestId: string }) => {
      const requestId = typeof data === 'string' ? data : data.requestId;
      const onlineUsers = Array.from(onlineUsersPerRequest.get(requestId) || []);
      socket.emit('presence:online', { requestId, userIds: onlineUsers });
    });

    socket.on('leave:request', (requestId: string) => {
      socket.leave(`request:${requestId}`);
      
      if (currentUserId && onlineUsersPerRequest.has(requestId)) {
        onlineUsersPerRequest.get(requestId)!.delete(currentUserId);
        if (onlineUsersPerRequest.get(requestId)!.size === 0) {
          onlineUsersPerRequest.delete(requestId);
        }
        // Broadcast leave to others
        socket.to(`request:${requestId}`).emit('presence:leave', { userId: currentUserId, requestId });
      }

      if (requestId === currentRequestId) {
        currentRequestId = null;
        currentUserId = null;
      }
    });

    socket.on('disconnect', () => {
      // Handle user going offline when connection drops
      if (currentRequestId && currentUserId && onlineUsersPerRequest.has(currentRequestId)) {
        onlineUsersPerRequest.get(currentRequestId)!.delete(currentUserId);
        if (onlineUsersPerRequest.get(currentRequestId)!.size === 0) {
          onlineUsersPerRequest.delete(currentRequestId);
        }
        // Broadcast disconnect to others in the room
        socket.to(`request:${currentRequestId}`).emit('presence:leave', { userId: currentUserId, requestId: currentRequestId });
      }
    });
  });
  return io;
}

export function emitToRequestRoom(requestId: string, event: string, payload: any) {
  if (!io) return;
  io.to(`request:${requestId}`).emit(event, payload);
}

export function emitToUser(userId: string, event: string, payload: any) {
  if (!io) return;
  io.to(`user:${userId}`).emit(event, payload);
}


