/**
 * 会议室 WebSocket 通信 Hook
 * 提供 WebSocket 连接和会议室加入流程的完整功能
 */

import { type ComputedRef, ref } from 'vue';
import { io } from 'socket.io-client';
import type { JoinMode, MeetingClientSocket, OnJoinRoomData, SendJoinRoomData, ServerToClientEvents } from './types';
import { getDefaultAuthHeaders } from '@/utils/axios/auth-headers';
import router from '@/router/router';
let socket: MeetingClientSocket | null = null;
/** 鉴权失败回调集合 */
const authFailedHandlers = new Set<(err: Error) => void>();
let baseOnConnect: (() => void) | null = null;
let baseOnDisconnect: ((reason: any) => void) | null = null;
let baseOnConnectError: ((error: any) => void) | null = null;

/**
 * 注册鉴权失败事件回调
 * @param handler - 回调函数
 * @returns 取消订阅函数
 */
export function onMeetingWsAuthFailed(handler: (err: Error) => void): () => void {
  authFailedHandlers.add(handler);
  return () => {
    authFailedHandlers.delete(handler);
  };
}

/**
 * 获取 Socket.IO 客户端实例（单例模式）
 */
export function useWebSocket(): MeetingClientSocket {
  if (!socket) {
    // 连接到 Socket.IO 的 meeting namespace
    socket = io(`${import.meta.env.VITE_MEETING_WEBSOCKET_BASE_URL}/meeting`, {
      transports: ['websocket'], // 优先使用 websocket
      reconnection: true, // 启用自动重连
      reconnectionAttempts: 5, // 最多重连 5 次
      reconnectionDelay: 1000, // 重连间隔 1 秒
      // 使用函数形式，确保每次连接/重连时都获取最新的 Token
      auth: (cb) => {
        const headers = getDefaultAuthHeaders();
        cb(headers);
      },
    }) as MeetingClientSocket;

    baseOnConnect = () => {
      console.log('[会议室] WebSocket 已连接:', socket?.id);
    };
    baseOnDisconnect = (reason) => {
      console.warn('[会议室] WebSocket 已断开:', reason);
      socket = null;
    };
    baseOnConnectError = (error) => {
      console.error('[会议室] WebSocket 连接错误:', error);
      const msg = String(error?.message || '');
      if (msg === 'Unauthorized') {
        try {
          socket?.disconnect();
        } catch {}
        router.replace('/login');
      }
    };

    socket.on('connect', baseOnConnect);
    socket.on('disconnect', baseOnDisconnect);
    socket.on('connect_error', baseOnConnectError);
  }
  return socket;
}

/**
 * 销毁会议室 WebSocket 实例
 * 移除所有事件监听并断开连接
 */
export function destroyMeetingSocket(): void {
  if (!socket) {
    authFailedHandlers.clear();
    baseOnConnect = null;
    baseOnDisconnect = null;
    baseOnConnectError = null;
    return;
  }
  if (baseOnConnect) {
    socket.off('connect', baseOnConnect);
  }
  if (baseOnDisconnect) {
    socket.off('disconnect', baseOnDisconnect);
  }
  if (baseOnConnectError) {
    socket.off('connect_error', baseOnConnectError);
  }
  try {
    socket.disconnect();
  } catch {}
  socket = null;
  authFailedHandlers.clear();
  baseOnConnect = null;
  baseOnDisconnect = null;
  baseOnConnectError = null;
}

/**
 * 会议室加入流程 Hook
 * 负责处理 WebSocket 加入房间、获取短 UID 等业务流程
 * @param {Ref<string>} courseRoomId - 课程房间 ID
 * @param {Ref<number>} longUserId - 用户长 ID
 * @param {Ref<string>} userName - 用户名称
 * @param {Ref<number>} homeworkId - 作业 ID
 * @returns {MeetingSocketReturn} 返回加入结果和控制方法
 */
export function useMeetingSocket(
  courseRoomId: ComputedRef<string>,
  longUserId: ComputedRef<number>,
  userName: ComputedRef<string>,
  homeworkId: ComputedRef<number>,
  joinMode: ComputedRef<JoinMode>,
  endTimestamp: ComputedRef<number | null>
) {
  /** 是否已加入房间 */
  const isJoined = ref(false);

  /**
   * 加入 WebSocket 房间并获取房间数据
   * @param {Socket} ws - Socket.IO 客户端实例
   */
  function joinRoom(ws: MeetingClientSocket): Promise<OnJoinRoomData> {
    return new Promise((resolve, reject) => {
      const sendJoin = () => {
        let timeoutId: NodeJS.Timeout | null = null;
        const messageHandler = (packet: Parameters<ServerToClientEvents['message']>[0]) => {
          if (packet.type === 'sev_join_room') {
            if (timeoutId) {
              clearTimeout(timeoutId);
            }
            ws.off('message', messageHandler);
            resolve(packet.data);
          } else if (packet.type === 'sev_kick_user') {
            if (timeoutId) {
              clearTimeout(timeoutId);
            }
            ws.off('message', messageHandler);
            reject(new Error(packet.data.reason || '您已被创建者移出会议'));
          }
        };
        timeoutId = setTimeout(() => {
          ws.off('message', messageHandler);
          reject(new Error('加入房间超时，请检查网络连接'));
        }, 10000);
        ws.on('message', messageHandler);
        const joinData: SendJoinRoomData = {
          courseRoomId: courseRoomId.value,
          userName: userName.value,
          homeworkId: homeworkId.value,
          joinMode: joinMode.value,
          ...(endTimestamp.value ? { endTimestamp: endTimestamp.value } : {}),
        };
        ws.emit('client_join_room', joinData);
      };

      if (ws.connected) {
        sendJoin();
      } else {
        const onConnect = () => {
          cleanupConnect();
          sendJoin();
        };

        const connectTimer = setTimeout(() => {
          cleanupConnect();
          reject(new Error('连接服务器超时，请检查网络'));
        }, 8000);

        /** 清理连接事件监听器 */
        function cleanupConnect() {
          ws.off('connect', onConnect);
          clearTimeout(connectTimer);
        }
        ws.once('connect', onConnect);
        ws.connect();
      }
    });
  }

  /**
   * 离开 WebSocket 房间
   * @param {Socket} ws - Socket.IO 客户端实例
   */
  function leaveRoom(ws: MeetingClientSocket): void {
    ws.emit('client_leave_room', { courseRoomId: courseRoomId.value });
  }

  /**
   * 续期房间结束时间（仅老师端）
   * @param ws - Socket.IO 客户端实例
   * @param roomId - 服务端房间 ID
   * @param nextEndTimestamp - 新的结束时间（毫秒时间戳）
   */
  function renewRoomEndTimestamp(ws: MeetingClientSocket, roomId: string, nextEndTimestamp: number): void {
    ws.emit('client_renew_room_end_timestamp', { roomId, endTimestamp: nextEndTimestamp });
  }

  return {
    isJoined,
    joinRoom,
    leaveRoom,
    renewRoomEndTimestamp,
  };
}

export type MeetingSocketReturn = ReturnType<typeof useMeetingSocket>;
