/**
 * WebSocket处理器
 * 负责管理WebSocket连接和信令消息处理
 */
import Offer from './offer';
import Answer from './answer';
import Candidate from './candidate';
import { log, LogLevel } from '../log';

/**
 * 是否为私有模式
 */
let isPrivate: boolean;

/**
 * 客户端连接映射
 * 键: WebSocket实例
 * 值: 该WebSocket的连接ID集合
 */
const clients: Map<WebSocket, Set<string>> = new Map<WebSocket, Set<string>>();

/**
 * 连接组结构
 * host: 主机WebSocket实例（第一个连接的客户端）
 * participants: 参与者WebSocket集合（后续连接的客户端）
 */
interface ConnectionGroup {
  host: WebSocket;
  participants: Set<WebSocket>;
}

interface UserInfo {
  id: string;
  name: string;
  avatar?: string;
}

interface OnlineUser {
  socketId: string;
  connectionId: string;
  participantId: string;
  role: 'host' | 'participant' | 'idle';
  userId: string;
  name: string;
  avatar: string;
}

/**
 * 连接组映射
 * 键: connectionId
 * 值: ConnectionGroup（1个host + 多个participants）
 */
const connectionGroup: Map<string, ConnectionGroup> = new Map<string, ConnectionGroup>();

/**
 * 获取或创建WebSocket会话的连接ID集合
 * @param session WebSocket会话实例
 * @returns 连接ID的Set集合
 */
function getOrCreateConnectionIds(session: WebSocket): Set<string> {
  let connectionIds = null;
  // 检查客户端是否已存在
  if (!clients.has(session)) {
    // 如果不存在，创建新的连接ID集合
    connectionIds = new Set<string>();
    // 将新的连接ID集合与客户端关联
    clients.set(session, connectionIds);
  }
  // 获取客户端的连接ID集合
  connectionIds = clients.get(session);
  // 返回连接ID集合
  return connectionIds;
}

/**
 * 重置处理器状态
 * @param mode 通信模式（public或private）
 */
function reset(mode: string): void {
  // 设置是否为私有模式
  isPrivate = mode == "private";
}

/**
 * 添加新的WebSocket连接
 * @param ws WebSocket连接实例
 */
function add(ws: WebSocket): void {
  // 为新连接创建空的连接ID集合
  const id = new Set<string>();
  clients.set(ws, id);
  (ws as any).socketId = (ws as any).socketId || `ws_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
  // 记录添加WebSocket连接的日志
  log(LogLevel.log, `Add WebSocket: ${ws.url}`);
}

/**
 * 判断WebSocket是否为指定连接组的host
 * @param ws WebSocket连接实例
 * @param connectionId 连接ID
 * @returns 是否为host
 */
function isHost(ws: WebSocket, connectionId: string): boolean {
  const group = connectionGroup.get(connectionId);
  return group != null && group.host === ws;
}

/**
 * 向连接组中除发送者外的所有成员发送消息
 * @param connectionId 连接ID
 * @param senderWs 发送者WebSocket实例
 * @param message 要发送的消息对象
 */
function broadcastToGroup(connectionId: string, senderWs: WebSocket, message: any): void {
  const group = connectionGroup.get(connectionId);
  if (!group) return;
  // 如果发送者是host，转发给所有participants
  if (senderWs === group.host) {
    group.participants.forEach(participantWs => {
      participantWs.send(JSON.stringify(message));
    });
  } else {
    // 如果发送者是participant，转发给host
    group.host.send(JSON.stringify(message));
  }
}

/**
 * 移除WebSocket连接
 * @param ws WebSocket连接实例
 */
function remove(ws: WebSocket): void {
  const connectionIds = clients.get(ws);
  if (!connectionIds) return;

  connectionIds.forEach(connectionId => {
    const group = connectionGroup.get(connectionId);
    if (group) {
      if (group.host === ws) {
        group.participants.forEach(participantWs => {
          participantWs.send(JSON.stringify({ type: "disconnect", connectionId: connectionId, reason: "host-left" }));
        });
        connectionGroup.delete(connectionId);
      } else {
        group.participants.delete(ws);
        // 包含participantId，让host能识别是哪个participant离开
        group.host.send(JSON.stringify({ type: "participant-left", connectionId: connectionId, participantId: (ws as any).participantId }));
      }
    }
    log(LogLevel.log, `Remove connectionId: ${connectionId}`);
  });

  clients.delete(ws);
}

/**
 * 处理连接请求（1对多模式）
 * 第一个连接的客户端成为host，后续连接的客户端成为participants
 * @param ws WebSocket连接实例
 * @param connectionId 连接ID
 */
function onConnect(ws: WebSocket, connectionId: string): void {
  let polite = true;
  // 为每个WebSocket生成唯一的participantId
  const participantId = (ws as any).participantId = (ws as any).participantId || `p_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;

  if (isPrivate) {
    if (connectionGroup.has(connectionId)) {
      const group = connectionGroup.get(connectionId);
      group.participants.add(ws);
      log(LogLevel.log, `Participant ${participantId} joined connectionId: ${connectionId}, total participants: ${group.participants.size}`);
      // 通知host有新participant加入
      group.host.send(JSON.stringify({ type: "participant-joined", connectionId: connectionId, participantId: participantId }));
    } else {
      connectionGroup.set(connectionId, { host: ws, participants: new Set<WebSocket>() });
      polite = false;
      log(LogLevel.log, `Host created connectionId: ${connectionId}`);
    }
  }

  const connectionIds = getOrCreateConnectionIds(ws);
  connectionIds.add(connectionId);
  const role = polite ? 'participant' : 'host';
  ws.send(JSON.stringify({ type: "connect", connectionId: connectionId, polite: polite, role: role, participantId: participantId }));
}

/**
 * 处理断开连接请求（1对多模式）
 * @param ws WebSocket连接实例
 * @param connectionId 连接ID
 */
function onDisconnect(ws: WebSocket, connectionId: string): void {
  // 获取连接的连接ID集合
  const connectionIds = clients.get(ws);
  if (connectionIds) {
    // 从集合中删除连接ID
    connectionIds.delete(connectionId);
  }

  // 处理连接组
  const group = connectionGroup.get(connectionId);
  if (group) {
    if (group.host === ws) {
      // host断开连接，通知所有participants房间已关闭，并删除连接组
      group.participants.forEach(participantWs => {
        participantWs.send(JSON.stringify({ type: "disconnect", connectionId: connectionId, reason: "host-left" }));
      });
      connectionGroup.delete(connectionId);
      log(LogLevel.log, `Host disconnected, room ${connectionId} deleted, notified ${group.participants.size} participants`);
    } else {
      // participant断开连接，从组中移除并通知host（使用participant-left类型，host不会关闭房间）
      group.participants.delete(ws);
      group.host.send(JSON.stringify({ type: "participant-left", connectionId: connectionId, participantId: (ws as any).participantId }));
      log(LogLevel.log, `Participant left connectionId: ${connectionId}, remaining participants: ${group.participants.size}`);
    }
  }

  // 向当前连接发送断开连接消息
  ws.send(JSON.stringify({ type: "disconnect", connectionId: connectionId }));
  //RemoveHeartbeat(ws);
  // 记录断开连接的日志
  log(LogLevel.log, `Disconnect connectionId: ${connectionId}`);
}

/**
 * 处理offer信令（1对多模式）
 * host的offer转发给所有participants，participant的offer转发给host
 * @param ws WebSocket连接实例
 * @param message 消息数据
 */
function onOffer(ws: WebSocket, message: any): void {
  const connectionId = message.connectionId as string;
  const newOffer = new Offer(message.sdp, Date.now(), false);

  if (isPrivate) {
    if (connectionGroup.has(connectionId)) {
      const group = connectionGroup.get(connectionId);
      const senderParticipantId = (ws as any).participantId;
      const targetParticipantId = message.participantId;
      if (group.host === ws) {
        // host发送offer给特定participant（多peer模式下按participantId路由）
        newOffer.polite = true;
        if (targetParticipantId) {
          // 路由到指定participant
          group.participants.forEach(participantWs => {
            if ((participantWs as any).participantId === targetParticipantId) {
              participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer, participantId: targetParticipantId }));
            }
          });
        } else {
          // 兼容：无目标时广播给所有participants
          group.participants.forEach(participantWs => {
            const pid = (participantWs as any).participantId;
            participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer, participantId: pid }));
          });
        }
      } else {
        // participant发送offer给host，携带该participant的participantId
        // host端应为impolite（polite=false），确保perfect negotiation中host优先
        newOffer.polite = false;
        group.host.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer, participantId: senderParticipantId }));
      }
    }
    return;
  }

  // 公共模式：创建新的连接组（如果不存在）
  if (!connectionGroup.has(connectionId)) {
    connectionGroup.set(connectionId, { host: ws, participants: new Set<WebSocket>() });
  }
  // 向所有其他客户端广播offer
  clients.forEach((_v, k) => {
    if (k == ws) {
      return;
    }
    k.send(JSON.stringify({ from: connectionId, to: "", type: "offer", data: newOffer }));
  });
}

/**
 * 处理answer信令（1对多模式）
 * participant的answer转发给host
 * @param ws WebSocket连接实例
 * @param message 消息数据
 */
function onAnswer(ws: WebSocket, message: any): void {
  const connectionId = message.connectionId as string;
  const connectionIds = getOrCreateConnectionIds(ws);
  connectionIds.add(connectionId);
  const newAnswer = new Answer(message.sdp, Date.now());

  if (!connectionGroup.has(connectionId)) {
    return;
  }

  const group = connectionGroup.get(connectionId);
  const senderParticipantId = (ws as any).participantId;
  // 从answer消息中获取目标participantId（host回复时指定）
  const targetParticipantId = message.participantId;

  if (group.host === ws) {
    // host发送answer给特定participant
    if (targetParticipantId) {
      group.participants.forEach(participantWs => {
        if ((participantWs as any).participantId === targetParticipantId) {
          participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer, participantId: targetParticipantId }));
        }
      });
    } else {
      // 兼容：没有targetParticipantId时广播给所有participants
      group.participants.forEach(participantWs => {
        participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer }));
      });
    }
  } else {
    // participant发送answer给host，携带自己的participantId
    group.host.send(JSON.stringify({ from: connectionId, to: "", type: "answer", data: newAnswer, participantId: senderParticipantId }));
  }
}

/**
 * 处理candidate信令（1对多模式）
 * host的candidate转发给所有participants，participant的candidate转发给host
 * @param ws WebSocket连接实例
 * @param message 消息数据
 */
function onCandidate(ws: WebSocket, message: any): void {
  const connectionId = message.connectionId;
  const candidate = new Candidate(message.candidate, message.sdpMLineIndex, message.sdpMid, Date.now());
  const senderParticipantId = (ws as any).participantId;
  const targetParticipantId = message.participantId;

  if (isPrivate) {
    if (connectionGroup.has(connectionId)) {
      const group = connectionGroup.get(connectionId);
      if (group.host === ws) {
        // host发送candidate给特定participant
        if (targetParticipantId) {
          group.participants.forEach(participantWs => {
            if ((participantWs as any).participantId === targetParticipantId) {
              participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate, participantId: targetParticipantId }));
            }
          });
        } else {
          group.participants.forEach(participantWs => {
            participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate }));
          });
        }
      } else {
        // participant发送candidate给host，携带自己的participantId
        group.host.send(JSON.stringify({ from: connectionId, to: "", type: "candidate", data: candidate, participantId: senderParticipantId }));
      }
    }
    return;
  }
}

function onCallConnectionId(ws: WebSocket, message: any): void {
  // 获取连接ID
  const connectionId = message.connectionId;
  const clientId = message.clientId;
  // 在1对多模式下，通知host有新的呼叫请求
  if (connectionGroup.has(connectionId)) {
    const group = connectionGroup.get(connectionId);
    if (group.host !== ws) {
      // participant发起呼叫，通知host
      group.host.send(JSON.stringify({ from: connectionId, to: "", type: "call-request", data: connectionId }));
    }
  } else {
    // 兼容旧的广播方式
    clients.forEach((_v, k) => {
      if (k === ws) {
        return;
      }
      if (_v == clientId) {
        k.send(JSON.stringify({ from: connectionId, to: "", type: "call-request", data: connectionId }));
      }
    });
  }
}


/**
 * 处理广播消息请求（1对多模式）
 * @param ws WebSocket连接实例
 * @param message 消息数据
 */
function onBroadcast(ws: WebSocket, message: any): void {
  const broadcastMessage = message.message;
  const targetConnectionId = message.targetConnectionId;

  if (targetConnectionId) {
    // 向指定连接组广播
    if (connectionGroup.has(targetConnectionId)) {
      const group = connectionGroup.get(targetConnectionId);
      // 向组内所有成员发送消息
      group.host.send(JSON.stringify({
        type: "broadcast",
        message: broadcastMessage,
        from: "server"
      }));
      group.participants.forEach(participantWs => {
        participantWs.send(JSON.stringify({
          type: "broadcast",
          message: broadcastMessage,
          from: "server"
        }));
      });
    }
  } else {
    // 全局广播：向所有客户端发送消息
    clients.forEach((_v, k) => {
      k.send(JSON.stringify({
        type: "broadcast",
        message: broadcastMessage,
        from: "server"
      }));
    });
  }
}

function AddHeartbeat(ws: WebSocket, connectionId: string) {
  // 初始化心跳检测
  (ws as any).lastActivity = Date.now();

  // 设置心跳检测定时器，每30秒发送一次ping
  (ws as any).heartbeatTimer = setInterval(() => {
    const now = Date.now();
    // 检查上次活动时间，如果超过60秒没有活动，关闭连接
    if (now - (ws as any).lastActivity > 10000) {
      log(LogLevel.warn, 'WebSocket connection timeout, closing...');
      clearInterval((ws as any).heartbeatTimer);
      //ws.close();
      onDisconnect(ws, connectionId);
    } else {
      // 发送ping消息
      ws.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: { type: "ping" }.toString() }));
      log(LogLevel.log, 'WebSocket connection heartbeat, lastActivity: ', (ws as any).lastActivity);
    }
  }, 3000);
}

function RemoveHeartbeat(ws: WebSocket) {
  // 清除心跳检测定时器
  if ((ws as any).heartbeatTimer) {
    clearInterval((ws as any).heartbeatTimer);
  }
}

/**
 * 处理获取所有连接ID的请求
 * @param ws WebSocket连接实例
 */
function onGetAllConnectionIds(): string[] {
  // 获取所有connectionId
  const connectionIds = Array.from(connectionGroup.keys());
  return connectionIds;
}

/**
 * 获取WebSocket连接当前角色
 * @param ws WebSocket连接实例
 * @param connectionIds 该连接关联的connectionId集合
 * @returns 用户角色
 */
function getSocketRole(ws: WebSocket, connectionIds: string[]): 'host' | 'participant' | 'idle' {
  for (const connectionId of connectionIds) {
    if (isHost(ws, connectionId)) {
      return 'host';
    }
    const group = connectionGroup.get(connectionId);
    if (group && group.participants.has(ws)) {
      return 'participant';
    }
  }
  return 'idle';
}

/**
 * 将WebSocket连接转换为在线用户信息
 * @param ws WebSocket连接实例
 * @returns 在线用户信息
 */
function toOnlineUser(ws: WebSocket): OnlineUser {
  const connectionIds = Array.from(clients.get(ws) || []);
  const userInfo = ((ws as any).userInfo || {}) as UserInfo;
  return {
    socketId: (ws as any).socketId || '',
    connectionId: connectionIds[0] || '',
    participantId: (ws as any).participantId || '',
    role: getSocketRole(ws, connectionIds),
    userId: userInfo.id || '',
    name: userInfo.name || '',
    avatar: userInfo.avatar || ''
  };
}

/**
 * 获取在线WebSocket用户列表
 * @param connectionId 可选的连接ID，传入时仅返回关联该房间的在线用户
 * @returns 在线用户列表
 */
function onGetOnlineUsers(connectionId?: string): OnlineUser[] {
  const onlineUsers: OnlineUser[] = [];
  clients.forEach((connectionIds, ws) => {
    const ids = Array.from(connectionIds);
    if (connectionId && !ids.includes(connectionId)) {
      return;
    }
    onlineUsers.push(toOnlineUser(ws));
  });
  return onlineUsers;
}

/**
 * 处理chat-message信令（1对多模式）
 * host的消息转发给所有participants，participant的消息转发给host
 * @param ws WebSocket连接实例
 * @param message 消息数据
 */
function onMessage(ws: WebSocket, message: any): void {
  // 获取连接ID
  const connectionId = message.connectionId;
  const chatMessage = message.message;
  const senderParticipantId = (ws as any).participantId;
  if (chatMessage && chatMessage.type === 'user-info' && chatMessage.data) {
    (ws as any).userInfo = {
      id: chatMessage.data.id || '',
      name: chatMessage.data.name || '匿名用户',
      avatar: chatMessage.data.avatar || ''
    };
  }
  chatMessage.participantId = senderParticipantId;
  chatMessage.connectionId = connectionId;
  if (connectionGroup.has(connectionId)) {
    const group = connectionGroup.get(connectionId);
    if (group.host === ws) {
      // host发送消息，转发给所有participants
      group.participants.forEach(participantWs => {
        participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: JSON.stringify(chatMessage) }));
      });
    } else {
      // participant发送消息，转发给host（附带participantId）和其他participants
      group.host.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: JSON.stringify(chatMessage), participantId: senderParticipantId }));
      // 同时转发给其他participants（排除发送者自身）
      group.participants.forEach(participantWs => {
        if (participantWs !== ws) {
          participantWs.send(JSON.stringify({ from: connectionId, to: "", type: "on-message", data: JSON.stringify(chatMessage), participantId: senderParticipantId }));
        }
      });
    }
  }
}

/**
 * 导出WebSocket处理器函数
 */
export { reset, add, remove, onConnect, onDisconnect, onOffer, onAnswer, onCandidate, onCallConnectionId, onBroadcast, onGetAllConnectionIds, onGetOnlineUsers, AddHeartbeat, RemoveHeartbeat, onMessage, isHost, broadcastToGroup, connectionGroup };
