import * as websocket from "ws";
import { Server as HttpServer } from 'http';
import { Server as HttpsServer } from 'https';
import * as handler from "./class/websockethandler";
import { log, LogLevel } from './log';

const VALID_MESSAGE_TYPES = new Set([
  "connect",
  "disconnect",
  "offer",
  "answer",
  "candidate",
  "ping",
  "pong",
  "broadcast",
  "call-request",
  "host-userInfo",
  "invite-call",
  "on-message",
]);

function sendJson(ws: WebSocket, payload: unknown): void {
  if (ws.readyState === ws.OPEN) {
    ws.send(JSON.stringify(payload));
  }
}

function toMessageText(raw: unknown): string | null {
  if (typeof raw === 'string') {
    return raw;
  }

  if (raw instanceof ArrayBuffer) {
    return Buffer.from(raw).toString('utf8');
  }

  if (Array.isArray(raw)) {
    return Buffer.concat(raw as Buffer[]).toString('utf8');
  }

  if (Buffer.isBuffer(raw)) {
    return raw.toString('utf8');
  }

  log(LogLevel.warn, 'WS ignored unsupported raw message payload');
  return null;
}

function parseWsMessage(raw: unknown): any | null {
  const text = toMessageText(raw);
  if (text == null) {
    return null;
  }

  try {
    const msg = JSON.parse(text);
    if (!msg || typeof msg !== 'object' || typeof msg.type !== 'string') {
      log(LogLevel.warn, 'WS ignored malformed message:', text);
      return null;
    }
    if (!VALID_MESSAGE_TYPES.has(msg.type)) {
      log(LogLevel.warn, `WS ignored unsupported message type: ${msg.type}`);
      return null;
    }
    return msg;
  } catch (error) {
    log(LogLevel.warn, 'WS ignored invalid JSON message:', error);
    return null;
  }
}

function hasConnectionId(msg: any): boolean {
  return typeof msg.connectionId === 'string' && msg.connectionId.length > 0;
}

function hasData(msg: any): boolean {
  return msg.data && typeof msg.data === 'object';
}

export default class WSSignaling {
  server: HttpServer | HttpsServer;
  wss: websocket.Server;

  /**
   * 构造函数，初始化WebSocket信令服务器
   * @param server HTTP服务器实例
   * @param mode 通信模式（public或private）
   */
  constructor(server: HttpServer | HttpsServer, mode: string) {
    // 保存服务器实例
    this.server = server;
    // 创建WebSocket服务器
    this.wss = new websocket.Server({ server });
    // 重置处理器，设置通信模式
    handler.reset(mode);

    /**
     * 监听WebSocket连接事件
     * @param ws WebSocket连接实例
     */
    this.wss.on('connection', (ws: WebSocket) => {
      // 添加新的WebSocket连接到处理器
      handler.add(ws);
      //handler.AddHeartbeat(ws);
      /**
       * 监听连接关闭事件
       */
      ws.onclose = (): void => {
        // 从处理器中移除关闭的连接
        handler.remove(ws);
        //handler.RemoveHeartbeat(ws);
      };

      /**
       * 监听消息事件
       * @param event 消息事件对象
       */
      ws.onmessage = (event: MessageEvent): void => {
        // 消息类型说明：
        // 1. connect, disconnect 消息格式：
        //    { type: "connect", connectionId: "连接ID" }
        //    { type: "disconnect", connectionId: "连接ID" }
        // 2. offer, answer, candidate 消息格式：
        //    {
        //      type: "offer",
        //      data: {
        //        from: "发送方连接ID",
        //        to: "接收方连接ID",
        //        data: "信令数据"
        //      }
        //    }
        // 3. broadcast 消息格式：
        //    {
        //      type: "broadcast",
        //      message: "广播消息内容",
        //      targetConnectionId: "目标连接ID（可选）"
        //    }

        // 解析消息数据
        const msg = parseWsMessage(event.data);
        // 检查消息是否有效
        if (!msg || !this) {
          return;
        }

        // 打印接收到的消息
        log(LogLevel.log, 'WS received:', msg);

        // 根据消息类型处理
        switch (msg.type) {
          case "connect":
            if (!hasConnectionId(msg)) return;
            handler.onConnect(ws, msg.connectionId);
            break;
          case "disconnect":
            if (!hasConnectionId(msg)) return;
            handler.onDisconnect(ws, msg.connectionId);
            break;
          case "offer":
            if (!hasData(msg)) return;
            if (msg.participantId !== undefined) msg.data.participantId = msg.participantId;
            handler.onOffer(ws, msg.data);
            break;
          case "answer":
            if (!hasData(msg)) return;
            if (msg.participantId !== undefined) msg.data.participantId = msg.participantId;
            handler.onAnswer(ws, msg.data);
            break;
          case "candidate":
            if (!hasData(msg)) return;
            if (msg.participantId !== undefined) msg.data.participantId = msg.participantId;
            handler.onCandidate(ws, msg.data);
            break;
          case "ping":
            sendJson(ws, { type: "pong" });
            break;
          case "pong":
            (ws as any).lastActivity = Date.now();
            break;
          case "broadcast":
            if (!hasData(msg)) return;
            handler.onBroadcast(ws, msg.data);
            break;
          case 'call-request':
            if (!hasData(msg)) return;
            handler.onCallConnectionId(ws, msg.data);
            break;
           case 'host-userInfo':
            if (!hasData(msg)) return;
            handler.onHostUserInfo(ws, msg.data);
            break;
          case 'invite-call':
            if (!hasData(msg)) return;
            handler.onInviteCall(ws, msg.data);
            break;
          // case 'invite-accepted':
          //   handler.onInviteAccepted(ws, msg.data);
          //   break;
          // case 'invite-rejected':
          //   handler.onInviteRejected(ws, msg.data);
          //   break;
          case 'on-message':
            if (!hasData(msg)) return;
            if (msg.from) msg.data.connectionId = msg.from;
            handler.onMessage(ws, msg.data);
            break;
          default:
            break;
        }
      };
    });
  }
}
