const { WebSocketServer } = require('ws');
const url = require('url');
const { registerRoomHandlers, onRoomEmpty } = require('./roomHandler');
const { registerScreenHandlers } = require('./screenHandler');

// 房间订阅表：roomId → Set<ws>（大屏端订阅者）
const roomSubscribers = new Map();
// 大屏配置表：screenName → ws
const screenSockets = new Map();

/**
 * 向指定房间内除发送者以外的所有订阅者广播消息
 */
function broadcastToRoom(roomId, event, data, excludeWs = null) {
  const subs = roomSubscribers.get(roomId);
  if (!subs) return;
  const raw = JSON.stringify({ event, data });
  for (const ws of subs) {
    if (ws !== excludeWs && ws.readyState === ws.OPEN) {
      ws.send(raw);
    }
  }
}

/**
 * 向指定大屏发送消息
 */
function sendToScreen(screenName, event, data) {
  const ws = screenSockets.get(screenName);
  if (ws && ws.readyState === ws.OPEN) {
    ws.send(JSON.stringify({ event, data }));
  }
}

/**
 * 让一个 ws 订阅某个房间
 */
function joinRoom(ws, roomId) {
  if (!roomSubscribers.has(roomId)) {
    roomSubscribers.set(roomId, new Set());
  }
  roomSubscribers.get(roomId).add(ws);
}

/**
 * 让一个 ws 退订所有房间
 * 若某房间内已无 minigame 角色的连接，触发 onRoomEmpty
 */
function leaveAllRooms(ws) {
  for (const [roomId, subs] of roomSubscribers) {
    // 只处理这个 ws 实际所在的房间
    if (!subs.has(ws)) continue;

    subs.delete(ws);
    if (subs.size === 0) {
      roomSubscribers.delete(roomId);
    }

    // 统计该房间内仍在线的 minigame 玩家
    const remainingPlayers = [...subs].filter(
      s => s.ctx && s.ctx.role === 'minigame' && s.readyState === s.OPEN
    );

    if (remainingPlayers.length === 0) {
      console.log(`[WS] 房间 ${roomId} 已无玩家在线，触发收尾`);
      onRoomEmpty(roomId).catch(err =>
        console.error(`[WS] onRoomEmpty 处理失败 roomId=${roomId}:`, err)
      );
    }
  }
}

/**
 * 获取房间内当前活跃订阅连接数。
 * role 为空时统计全部角色，传入后只统计指定角色。
 */
function getActiveRoomSubscriberCount(roomId, role = null) {
  const subs = roomSubscribers.get(String(roomId));
  if (!subs) return 0;

  return [...subs].filter(ws => {
    if (ws.readyState !== ws.OPEN) return false;
    if (!role) return true;
    return ws.ctx?.role === role;
  }).length;
}

/**
 * 初始化原生 WebSocket 服务，挂载到 /ws 路径
 */
function initSocket(httpServer) {
  const wss = new WebSocketServer({ server: httpServer, path: '/ws' });

  wss.on('connection', (ws, req) => {
    const clientIp = req.socket.remoteAddress;
    console.log(`[WS] 客户端连接: ${clientIp}`);

    // 每个 ws 上挂载上下文数据
    ws.ctx = {
      role: null,       // 'minigame' | 'screen' | 'admin'
      roomId: null,
      sessionId: null,
      screenName: null,
    };

    // 统一工具函数：向当前 ws 发送事件（不能覆盖原生 emit，用独立方法）
    ws.sendEvent = (event, data) => {
      if (ws.readyState === ws.OPEN) {
        ws.send(JSON.stringify({ event, data }));
      }
    };

    // 注册业务处理器
    const roomCleanup = registerRoomHandlers(ws, {
      broadcastToRoom,
      joinRoom,
      leaveAllRooms,
      roomSubscribers,
    });

    const screenCleanup = registerScreenHandlers(ws, {
      sendToScreen,
      joinRoom,
      leaveAllRooms,
      screenSockets,
      broadcastToRoom,
    });

    ws.on('message', (raw) => {
      const str = raw.toString();
      // console.log('[WS] 收到消息:', str);
      let msg;
      try {
        msg = JSON.parse(str);
      } catch (e) {
        console.error('[WS] JSON解析失败:', e.message, '原始内容:', str);
        return;
      }
      const { event, data } = msg || {};
      console.log('[WS] 分发事件:', event, '注册的handlers:', ws._handlers ? Object.keys(ws._handlers) : 'null');
      if (!event) return;

      // 分发给各 handler
      ws._handlers && ws._handlers[event] && ws._handlers[event](data);
    });

    ws.on('close', () => {
      const role = ws.ctx.role || 'unknown';
      const extra = ws.ctx.roomId ? ` roomId=${ws.ctx.roomId}` : '';
      console.log(`[WS] 客户端断开: role=${role}${extra}`);
      roomCleanup();
      screenCleanup();
      leaveAllRooms(ws);
    });

    ws.on('error', (err) => {
      console.error('[WS] 连接错误:', err.message);
    });
  });

  console.log('[WS] 初始化完成，路径: /ws');
  return wss;
}

module.exports = {
  initSocket,
  broadcastToRoom,
  sendToScreen,
  joinRoom,
  leaveAllRooms,
  getActiveRoomSubscriberCount,
  screenSockets,
};
