<template>
  <div class="page">
    <div class="page-content">
      <div class="top-bar">
        <div class="room-info">
          <icon-mdi-video class="room-icon"></icon-mdi-video>
          <div class="room-details">
            <span class="room-name">{{ courseRoomName }}</span>
            <span class="room-id">房间 ID: {{ courseRoomId }}</span>
          </div>
        </div>
        <div class="network-status" :class="networkQualityClass">
          <icon-mdi-wifi class="network-icon"></icon-mdi-wifi>
          <span class="network-text">{{ networkStatusText }}</span>
        </div>
        <!-- 创建者标识 -->
        <div v-if="isHost" class="host-badge">创建者</div>
      </div>
      <div class="video-container">
        <!-- 未开课时显示空状态 -->
        <el-empty v-if="classStatus !== 'in_class'" :description="classStatus === 'not_started' ? '等待老师上课...' : '老师已下课，等待重新上课...'"></el-empty>
        <!-- 普通视频网格 -->
        <el-scrollbar v-else class="video-scrollbar" viewStyle="width: 100%; min-height: 100%; box-sizing: border-box; padding: 20px;">
          <div ref="remotePlayersContainer" class="remote-videos" :class="{ 'compact-mode': isCompact }" :style="{ '--grid-min-size': gridMinSize }">
            <div
              v-for="user in displayUsers"
              :key="user.uid"
              class="video-item"
              :class="{ 'is-local': user.isLocal, 'is-main-video': mainVideoUid === user.uid, 'is-screen-sharing': isUserScreenSharing(user.uid) }"
              @click="handleVideoClick(user)"
            >
              <div :id="user.isLocal ? 'player-local' : `player-${user.uid}`" class="player-wrapper"></div>
              <div class="user-label">
                <icon-mdi-account v-if="user.isLocal" class="local-icon"></icon-mdi-account>
                <span>{{ user.userName }}</span>
                <!-- 流类型标识 -->
                <span v-if="!user.isLocal" class="stream-type-badge">
                  {{ getUserStreamType(user.uid) === 0 ? '高' : '低' }}
                </span>
                <!-- 投屏标识 -->
                <span v-if="isUserScreenSharing(user.uid)" class="screen-sharing-badge">
                  <icon-mdi-monitor-share class="screen-sharing-icon"></icon-mdi-monitor-share>
                  投屏中
                </span>
              </div>
              <!-- 创建者控制按钮：常驻显示于视频右上角 -->
              <div v-if="isHost" class="user-controls">
                <!-- 设为主讲按钮：所有视频都显示（包括自己） -->
                <button
                  class="control-icon-btn primary"
                  :class="{ active: mainVideoUid === user.uid }"
                  :title="mainVideoUid === user.uid ? '取消主讲' : '设为主讲'"
                  @click.stop="handleSetMainScreen(user.uid)"
                >
                  <icon-mdi-monitor></icon-mdi-monitor>
                </button>
                <!-- 禁麦/禁视频/踢人按钮：只显示在其他用户视频上 -->
                <template v-if="!user.isLocal">
                  <button
                    class="control-icon-btn"
                    :class="{ muted: isUserAudioMuted(user.uid) }"
                    :title="isUserAudioMuted(user.uid) ? '解除禁麦' : '禁麦'"
                    @click.stop="toggleUserAudio(user.uid)"
                  >
                    <icon-mdi-microphone-off v-if="isUserAudioMuted(user.uid)"></icon-mdi-microphone-off>
                    <icon-mdi-microphone v-else></icon-mdi-microphone>
                  </button>
                  <button
                    class="control-icon-btn"
                    :class="{ muted: isUserVideoMuted(user.uid) }"
                    :title="isUserVideoMuted(user.uid) ? '解除禁视频' : '禁视频'"
                    @click.stop="toggleUserVideo(user.uid)"
                  >
                    <icon-mdi-video-off v-if="isUserVideoMuted(user.uid)"></icon-mdi-video-off>
                    <icon-mdi-video v-else></icon-mdi-video>
                  </button>
                  <button class="control-icon-btn kick" title="踢出会议" @click.stop="handleKickUser(user.uid)">
                    <icon-mdi-exit-to-app></icon-mdi-exit-to-app>
                  </button>
                </template>
              </div>
            </div>
          </div>
        </el-scrollbar>
      </div>
      <div class="control-bar">
        <!-- 上课/下课按钮（仅教师端显示，合并为一个按钮） -->
        <button v-if="isHost" class="control-btn" :class="{ danger: classStatus === 'in_class' }" @click="toggleClassStatus">
          <icon-mdi-monitor v-if="classStatus !== 'in_class'"></icon-mdi-monitor>
          <icon-mdi-exit-to-app v-else></icon-mdi-exit-to-app>
          <span class="btn-text">{{ classStatus === 'in_class' ? '下课' : '上课' }}</span>
        </button>
        <button v-if="isHost" class="control-btn" @click="openBlacklist">
          <icon-mdi-monitor></icon-mdi-monitor>
          <span class="btn-text">黑名单</span>
        </button>
        <button class="control-btn" :class="{ active: !isMuted }" :disabled="muteLoading" @click="toggleMute">
          <icon-mdi-loading v-if="muteLoading" class="animate-spin"></icon-mdi-loading>
          <template v-else>
            <icon-mdi-microphone v-if="!isMuted"></icon-mdi-microphone>
            <icon-mdi-microphone-off v-else></icon-mdi-microphone-off>
          </template>
          <span class="btn-text">{{ isMuted ? '取消静音' : '静音' }}</span>
        </button>
        <button class="control-btn" :class="{ active: !isVideoOff }" :disabled="videoLoading" @click="toggleVideo">
          <icon-mdi-loading v-if="videoLoading" class="animate-spin"></icon-mdi-loading>
          <template v-else>
            <icon-mdi-video v-if="!isVideoOff"></icon-mdi-video>
            <icon-mdi-video-off v-else></icon-mdi-video-off>
          </template>
          <span class="btn-text">{{ isVideoOff ? '开启视频' : '关闭视频' }}</span>
        </button>
        <!-- 投屏按钮（仅创建者显示，仅上课中可用） -->
        <button
          v-if="isHost"
          class="control-btn"
          :class="{ active: isScreenSharing }"
          :disabled="screenShareLoading || classStatus !== 'in_class'"
          @click="toggleScreenShare"
        >
          <icon-mdi-loading v-if="screenShareLoading" class="animate-spin"></icon-mdi-loading>
          <icon-mdi-monitor-share v-else></icon-mdi-monitor-share>
          <span class="btn-text">{{ isScreenSharing ? '停止投屏' : '开始投屏' }}</span>
        </button>
        <!-- 离开按钮（仅学生端显示，教师端用下课按钮离开） -->
        <button v-if="!isHost" class="control-btn danger" :disabled="leaveLoading" @click="handleLeave">
          <icon-mdi-loading v-if="leaveLoading" class="animate-spin"></icon-mdi-loading>
          <icon-mdi-exit-to-app v-else></icon-mdi-exit-to-app>
          <span class="btn-text">离开</span>
        </button>
      </div>
      <el-drawer v-model="blacklistVisible" title="黑名单" size="30%">
        <div v-if="blacklist.length === 0" class="empty">暂无黑名单</div>
        <ul v-else class="blacklist">
          <li v-for="item in blacklist" :key="item.shortUid" class="blacklist-item">
            <span class="user-info">{{ item.userName }} (ID: {{ item.shortUid }})</span>
            <button class="remove-btn" @click="removeFromBlacklist(item.shortUid)">解除</button>
          </li>
        </ul>
      </el-drawer>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { playPromptTone } from '@/utils/prompt-tone';
  import { useResizeObserver } from '@vueuse/core';
  import IconMdiAccount from '~icons/mdi/account';
  import IconMdiExitToApp from '~icons/mdi/exit-to-app';
  import IconMdiLoading from '~icons/mdi/loading';
  import IconMdiMicrophone from '~icons/mdi/microphone';
  import IconMdiMicrophoneOff from '~icons/mdi/microphone-off';
  import IconMdiVideo from '~icons/mdi/video';
  import IconMdiVideoOff from '~icons/mdi/video-off';
  import IconMdiMonitor from '~icons/mdi/monitor';
  import IconMdiMonitorShare from '~icons/mdi/monitor-share';
  import IconMdiWifi from '~icons/mdi/wifi';
  import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue';
  import { useRoute, useRouter } from 'vue-router';
  import { useStreamType } from './useStreamType';
  import { usePermissions } from './usePermissions';
  import type { DisplayUser, OnJoinRoomData, ServerToClientEvents } from './types';
  import { useAgoraMeeting } from './useAgoraMeeting';
  import { destroyMeetingSocket, useMeetingSocket, useWebSocket } from './websocket';
  import { ElMessageBox, ElMessage as Toast } from 'element-plus';
  import { getBlacklistAxios, removeFromBlacklistAxios } from '@/api/meeting';

  const route = useRoute();
  const router = useRouter();
  const userStore = useUserStore();

  /** 课程房间名称 */
  const courseRoomName = computed(() => String(route.query.name || ''));
  /** 用户长 ID */
  const longUserId = computed(() => userStore.userInfo?.id || 0);
  /** 用户名称 */
  const userName = computed(() => userStore.userInfo?.name || '用户');
  /** 预计时长 */
  const meetingDuration = computed(() => Number(route.query.meetingDuration || '40'));
  /** 作业 ID（用于服务端查询老师长 ID 并维护 teacherUid） */
  const homeworkId = computed(() => Number(route.query.homeworkId || 0));
  /** 课程房间ID */
  const courseRoomId = computed(() => `n_${homeworkId.value}`);
  /** 用户角色（学生或教师） */
  const role = ref<'student' | 'teacher'>(String(route.query.role) === 'student' ? 'student' : 'teacher');
  /** 是否为创建者 */
  const isHost = computed(() => role.value === 'teacher');

  /** 远程播放器容器 DOM 元素 */
  const remotePlayersContainer = ref<HTMLDivElement | null>(null);
  /** 视频网格最小列宽 */
  const gridMinSize = ref('320px');
  /** 是否为紧凑模式（单列布局），此时首项不跨列 */
  const isCompact = ref(false);

  useResizeObserver(remotePlayersContainer, (entries) => {
    const entry = entries[0];
    const { width } = entry.contentRect;
    const targetColumnWidth = (width - 48) / 4;
    const minSize = Math.max(210, Math.min(320, targetColumnWidth));
    gridMinSize.value = `${Math.floor(minSize)}px`;
    isCompact.value = width < minSize * 2 + 16;
  });
  /** 课堂状态：not_started表示未开始 / in_class表示上课中 / finished表示已结束*/
  const classStatus = ref<'finished' | 'in_class' | 'not_started'>('not_started');
  /** 用户的短id */
  const shortUid = ref<number>(0);
  /** 房间号 */
  const roomId = ref<string>(''); // 房间 ID 会在加入后设置
  const socket = useWebSocket();
  const screenShareOwnerUid = ref<number | string | null>(null);
  const screenShareStreamUid = ref<number | null>(null);
  const meeting = useAgoraMeeting(courseRoomId, userName, shortUid, {
    onTokenWillExpire: () => {
      Toast.warning('Token 即将过期，正在自动刷新...');
    },
    onTokenExpired: () => {
      Toast.error('Token 已过期，请重新加入会议');
    },
    onConnectionStateChange: (state: string) => {
      if (state === 'DISCONNECTED') {
        Toast.warning('网络连接已断开');
      } else if (state === 'RECONNECTING') {
        Toast.info('正在重新连接...');
      }
    },
    onError: (error: Error) => {
      Toast.error(error.message || '操作失败');
    },
    onLeft: () => {
      Toast.success('已离开会议');
    },
    onLocalScreenShareEnded: () => {
      if (roomId.value) {
        socket.emit('client_stop_screen_share', { roomId: roomId.value, screenUid: screenShareStreamUid.value || undefined });
      }
      screenShareOwnerUid.value = null;
      screenShareStreamUid.value = null;
      Toast.info?.('已停止投屏');
    },
  });
  const permissions = usePermissions(socket, roomId);

  // 使用会议加入流程 Hook
  /** 加入方式 */
  const joinMode = computed(() => 'classroom' as const);
  /** 房间结束时间（毫秒时间戳） */
  const endTimestamp = computed(() => {
    const t = Number(route.query.endTimestamp || 0);
    return Number.isFinite(t) && t > 0 ? t : null;
  });
  const meetingSocket = useMeetingSocket(courseRoomId, longUserId, userName, homeworkId, joinMode, endTimestamp);
  // 使用双流模式 Hook
  const streamType = useStreamType(meeting.client);
  // 从 Hook 中解构所需变量
  const {
    isMuted,
    isVideoOff,
    muteLoading,
    videoLoading,
    leaveLoading,
    networkQuality,
    selectedUserUid,
    toggleMute,
    toggleVideo,
    leaveChannel,
    cleanup,
    startScreenShare,
    stopScreenTrack,
    isScreenSharing,
  } = meeting;

  // 解构双流模式
  const { mainVideoUid, getUserStreamType, toggleMainVideo } = streamType;

  // 解构权限管理
  const { isUserAudioMuted, isUserVideoMuted, muteUserAudio, unmuteUserAudio, muteUserVideo, unmuteUserVideo, kickUser, setMainVideoGlobal } = permissions;

  /** 投屏操作加载状态 */
  const screenShareLoading = ref(false);
  /** 房间结束时间（毫秒时间戳） */
  const roomEndTimestamp = ref<number | null>(null);
  /** 是否正在弹出续期窗口 */
  const isRenewPrompting = ref<boolean>(false);

  watchEffect(() => {
    if (roomEndTimestamp.value === null && endTimestamp.value) {
      roomEndTimestamp.value = endTimestamp.value;
    }
  });

  /**
   * 检查用户是否正在投屏
   * @param {number | string} uid - 用户 ID
   */
  function isUserScreenSharing(uid: number | string): boolean {
    return screenShareOwnerUid.value === uid;
  }

  let isLeaving = false;

  // WebSocket 消息分发（依赖 meetingSocket/roomId/streamType 已定义）
  type MeetingWsPacket = Parameters<ServerToClientEvents['message']>[0];
  const onSocketMessage = (packet: MeetingWsPacket) => {
    console.log('[会议室] 收到控制消息:', packet);
    switch (packet.type) {
      case 'sev_join_room': {
        const data = packet.data;
        if (data?.roomId && !roomId.value) {
          roomId.value = data.roomId;
        }
        if (data?.classStatus) {
          classStatus.value = data.classStatus;
        }
        if (typeof data?.endTimestamp === 'number') {
          roomEndTimestamp.value = data.endTimestamp;
        }
        if (typeof data?.screenShareUid === 'number') {
          screenShareStreamUid.value = data.screenShareUid;
        }
        if (typeof data?.screenShareOwnerUid === 'number') {
          screenShareOwnerUid.value = data.screenShareOwnerUid;
        }

        if (screenShareOwnerUid.value === shortUid.value && isHost.value && !meeting.isScreenSharing.value) {
          setTimeout(async () => {
            if (meeting.isJoined.value && classStatus.value === 'in_class') {
              try {
                const screenUid = await startScreenShare();
                socket.emit('client_start_screen_share', { roomId: roomId.value, screenUid });
                screenShareStreamUid.value = screenUid;
                screenShareOwnerUid.value = shortUid.value;
                Toast.info?.('已恢复投屏状态');
              } catch {}
            }
          }, 1000);
        }
        break;
      }
      case 'sev_room_end_timestamp_updated': {
        roomEndTimestamp.value = packet.data.endTimestamp;
        Toast.success('课程已续期');
        break;
      }
      case 'sev_room_will_expire': {
        if (isRenewPrompting.value) {
          break;
        }
        isRenewPrompting.value = true;
        const baseEndTimestamp = roomEndTimestamp.value || packet.data.endTimestamp;
        const minutesLeft = Math.max(1, Math.ceil(packet.data.remainingMs / 60000));
        ElMessageBox.prompt(`课程将在约 ${minutesLeft} 分钟后结束，是否续期？`, '续期', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          inputValue: '10',
          inputPattern: /^\d+$/,
          inputErrorMessage: '请输入分钟数',
        })
          .then(({ value }) => {
            const minutes = Number(value);
            if (!Number.isFinite(minutes) || minutes <= 0) {
              Toast.error('续期分钟数不正确');
              return;
            }
            const nextEndTimestamp = baseEndTimestamp + minutes * 60 * 1000;
            meetingSocket.renewRoomEndTimestamp(socket, packet.data.fromRoomId, nextEndTimestamp);
          })
          .catch(() => {})
          .finally(() => {
            isRenewPrompting.value = false;
          });
        break;
      }
      case 'sev_mute_audio': {
        // 教师端收到禁麦消息时，更新本地权限状态（同步其他用户被禁麦的状态）
        if (isHost.value) {
          const targetUid = packet.data?.targetUid;
          if (targetUid) {
            permissions.userPermissions.set(targetUid, {
              uid: targetUid,
              isAudioMuted: true,
              isVideoMuted: permissions.userPermissions.get(targetUid)?.isVideoMuted ?? false,
              isKicked: permissions.userPermissions.get(targetUid)?.isKicked ?? false,
            });
          }
        } else {
          meeting.forceMuteAudio();
          Toast.warning('您已被创建者禁麦');
        }
        break;
      }
      case 'sev_unmute_audio': {
        // 教师端收到解除禁麦消息时，更新本地权限状态
        if (isHost.value) {
          const targetUid = packet.data?.targetUid;
          if (targetUid) {
            permissions.userPermissions.set(targetUid, {
              uid: targetUid,
              isAudioMuted: false,
              isVideoMuted: permissions.userPermissions.get(targetUid)?.isVideoMuted ?? false,
              isKicked: permissions.userPermissions.get(targetUid)?.isKicked ?? false,
            });
          }
        } else {
          meeting.forceUnmuteAudio();
          Toast.success('您已被解除禁麦');
        }
        break;
      }
      case 'sev_mute_video': {
        // 教师端收到禁视频消息时，更新本地权限状态
        if (isHost.value) {
          const targetUid = packet.data?.targetUid;
          if (targetUid) {
            permissions.userPermissions.set(targetUid, {
              uid: targetUid,
              isAudioMuted: permissions.userPermissions.get(targetUid)?.isAudioMuted ?? false,
              isVideoMuted: true,
              isKicked: permissions.userPermissions.get(targetUid)?.isKicked ?? false,
            });
          }
        } else {
          meeting.forceMuteVideo();
          Toast.warning('您已被创建者禁视频');
        }
        break;
      }
      case 'sev_unmute_video': {
        // 教师端收到解除禁视频消息时，更新本地权限状态
        if (isHost.value) {
          const targetUid = packet.data?.targetUid;
          if (targetUid) {
            permissions.userPermissions.set(targetUid, {
              uid: targetUid,
              isAudioMuted: permissions.userPermissions.get(targetUid)?.isAudioMuted ?? false,
              isVideoMuted: false,
              isKicked: permissions.userPermissions.get(targetUid)?.isKicked ?? false,
            });
          }
        } else {
          meeting.forceUnmuteVideo();
          Toast.success('您已被解除禁视频');
        }
        break;
      }
      case 'sev_set_main_video': {
        const targetUid = packet.data.targetUid;
        selectedUserUid.value = targetUid;
        streamType.setMainVideo(targetUid);
        Toast.info?.('已切换主屏');
        break;
      }
      case 'sev_start_screen_share': {
        const targetUid = packet.data?.targetUid;
        const screenUid = packet.data?.screenUid;
        console.log('[屏幕共享] 收到 sev_start_screen_share 消息:', targetUid);
        if (typeof targetUid === 'number') {
          screenShareOwnerUid.value = targetUid;
          if (typeof screenUid === 'number') {
            screenShareStreamUid.value = screenUid;
          }
          console.log('[屏幕共享] screenShareOwnerUid 已设置为:', targetUid);
          Toast.info?.(`用户 ${targetUid} 开始投屏`);
        }
        break;
      }
      case 'sev_stop_screen_share': {
        const targetUid = packet.data?.targetUid;
        if (typeof targetUid === 'number') {
          screenShareOwnerUid.value = null;
          screenShareStreamUid.value = null;
          Toast.info?.(`用户 ${targetUid} 停止投屏`);
        }
        break;
      }
      case 'sev_class_started': {
        classStatus.value = 'in_class';
        Toast.success('老师已上课，准备加入音视频');
        meetingSocket
          .joinRoom(socket)
          .then(async (roomData: OnJoinRoomData) => {
            roomId.value = roomData.roomId;
            shortUid.value = roomData.shortUid;
            await meeting.initAndJoin(roomData);
          })
          .catch((e: unknown) => {
            if (e instanceof Error && e.message) {
              Toast.error(e.message);
              return;
            }
            Toast.error('加入失败');
          });
        break;
      }
      case 'sev_class_ended': {
        classStatus.value = 'finished';
        // 下课时自动停止投屏
        if (isScreenSharing.value) {
          stopScreenTrack().catch(() => {});
          screenShareOwnerUid.value = null;
          screenShareStreamUid.value = null;
        }
        leaveChannel().catch(() => {});
        Toast.info?.('老师已下课，等待重新上课');
        break;
      }
      case 'sev_kick_user': {
        // 教师端收到踢人消息时，更新本地权限状态
        if (isHost.value) {
          const targetUid = packet.data?.targetUid;
          if (targetUid) {
            permissions.userPermissions.set(targetUid, {
              uid: targetUid,
              isAudioMuted: permissions.userPermissions.get(targetUid)?.isAudioMuted ?? false,
              isVideoMuted: permissions.userPermissions.get(targetUid)?.isVideoMuted ?? false,
              isKicked: true,
            });
          }
        } else {
          // 学生端：被创建者踢出
          Toast.error('您已被创建者移出会议');
          handleLeave();
        }
        break;
      }
      case 'sev_device_conflict': {
        // 多设备登录被踢出
        playPromptTone();
        Toast.warning('您已在其他设备进入课程');
        handleLeave();
        break;
      }
      default:
        console.log('未处理的消息====', packet);
        break;
    }
  };
  socket.on('message', onSocketMessage);

  /** 网络质量样式类名 */
  const networkQualityClass = computed(() => networkQuality.value);

  /** 网络状态文本描述 */
  const networkStatusText = computed(() => {
    const qualityMap: Record<string, string> = {
      good: '网络良好',
      fair: '网络一般',
      poor: '网络较差',
    };
    return qualityMap[networkQuality.value] || '网络未知';
  });

  /** 显示的用户列表（包含本地用户和选中的主视频用户排序） */
  const displayUsers = computed<DisplayUser[]>(() => {
    const users: DisplayUser[] = (meeting.remoteUsers.value as DisplayUser[]) || [];
    const result: DisplayUser[] = [...users];
    // 只有当声网分配了真实 uid 后才显示本地用户（避免 uid 变化导致 DOM 重建）
    // 使用 meeting.localUser 确保响应式追踪
    const localUid = meeting.localUser.uid;
    if (localUid && localUid !== 0) {
      result.push(meeting.localUser as DisplayUser);
    }

    if (selectedUserUid.value) {
      const idx = result.findIndex((u) => u.uid === selectedUserUid.value);
      if (idx > 0) {
        const [selected] = result.splice(idx, 1);
        result.unshift(selected);
      }
    }
    return result;
  });

  /** 黑名单抽屉可见性 */
  const blacklistVisible = ref(false);
  /** 黑名单列表（包含短 UID 和名称） */
  interface BlacklistItem {
    shortUid: number;
    userName: string;
  }
  const blacklist = ref<BlacklistItem[]>([]);

  // 短 UID 变量重命名避免冲突（已在上面定义）
  // shortUid 已在 182 行定义，这里不需要重复

  /** 打开黑名单抽屉并加载数据 */
  async function openBlacklist(): Promise<void> {
    if (!isHost.value) {
      return;
    }
    if (!roomId.value) {
      Toast.warning('房间未就绪，稍后再试');
      return;
    }
    blacklistVisible.value = true;
    try {
      const res = await getBlacklistAxios(roomId.value);
      // 后端返回的是包含 shortUid 和 userName 的数组
      const data = res.data as unknown;
      blacklist.value = (Array.isArray(data) ? data : []) as BlacklistItem[];
    } catch (e) {
      Toast.error('获取黑名单失败');
    }
  }

  /** 解除黑名单 */
  async function removeFromBlacklist(itemShortUid: number): Promise<void> {
    if (!roomId.value) {
      return;
    }
    try {
      await removeFromBlacklistAxios(roomId.value, itemShortUid);
      blacklist.value = blacklist.value.filter((item) => item.shortUid !== itemShortUid);
      Toast.success('已解除');
    } catch (e) {
      Toast.error('解除失败');
    }
  }

  /**
   * 切换课程状态（上课/下课合并按钮）
   * 下课时发送下课消息并离开
   */
  function toggleClassStatus(): void {
    if (!isHost.value || !roomId.value) {
      return;
    }
    if (classStatus.value === 'in_class') {
      // 下课：发送下课消息并离开
      endClassAndLeave();
    } else {
      // 上课：发送上课消息
      socket.emit('client_start_class', { roomId: roomId.value, meetingDuration: meetingDuration.value });
    }
  }

  /**
   * 下课并离开（教师专用)
   * 先发送下课消息，然后离开会议
   */
  async function endClassAndLeave(): Promise<void> {
    if (isLeaving) {
      return;
    }
    isLeaving = true;
    try {
      // 先发送下课消息
      socket.emit('client_end_class', { roomId: roomId.value });
      // 等待一小段时间确保消息发送成功
      await new Promise<void>((resolve) => {
        setTimeout(() => {
          resolve();
        }, 100);
      });
      // 移除 WebSocket 消息监听
      socket.off('message', onSocketMessage);
      // 离开 WebSocket 房间
      try {
        meetingSocket.leaveRoom(socket);
      } catch {}
      // 销毁 WebSocket 连接
      destroyMeetingSocket();
      // 离开声网会议并清理所有资源
      await cleanup();
      // 返回上一页
      router.back();
    } catch (error) {
      console.error('下课离开失败:', error);
      // eslint-disable-next-line require-atomic-updates
      isLeaving = false;
      throw error;
    }
  }

  /**
   * 处理视频点击：将点击的视频切换到主位置
   * @param {DisplayUser} user - 被点击的用户对象
   */
  async function handleVideoClick(user: DisplayUser): Promise<void> {
    if (selectedUserUid.value === user.uid) {
      selectedUserUid.value = null;
    } else {
      selectedUserUid.value = user.uid;
    }
    // 切换主视频并更新流类型
    await toggleMainVideo(user.uid);
  }

  /**
   * 切换投屏状态
   */
  async function toggleScreenShare(): Promise<void> {
    if (screenShareLoading.value) {
      return;
    }
    screenShareLoading.value = true;
    try {
      console.log('[投屏] toggleScreenShare 被调用，isScreenSharing.value:', isScreenSharing.value);
      if (isScreenSharing.value) {
        const currentScreenUid = screenShareStreamUid.value || meeting.localScreenUid.value || undefined;
        // 停止投屏
        await stopScreenTrack();
        // 通知后端停止投屏
        socket.emit('client_stop_screen_share', { roomId: roomId.value, screenUid: currentScreenUid });
        // 清除投屏状态
        screenShareOwnerUid.value = null;
        screenShareStreamUid.value = null;
        Toast.success('已停止投屏');
      } else {
        // 开始投屏
        console.log('[投屏] 准备开始投屏');
        const screenUid = await startScreenShare();
        // 通知后端开始投屏（带上自己的短 UID）
        socket.emit('client_start_screen_share', { roomId: roomId.value, shortUid: shortUid.value, screenUid });
        // 设置投屏状态
        screenShareOwnerUid.value = shortUid.value;
        screenShareStreamUid.value = screenUid;
        Toast.success('已开始投屏');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : '操作失败';
      if (errorMessage !== '已取消屏幕共享') {
        Toast.error(errorMessage);
      }
    } finally {
      screenShareLoading.value = false;
    }
  }

  /**
   * 切换用户音频（禁麦/解除禁麦）
   * @param {number | string} uid - 用户 ID
   */
  function toggleUserAudio(uid: number | string): void {
    if (isUserAudioMuted(uid)) {
      unmuteUserAudio(uid);
    } else {
      muteUserAudio(uid);
    }
  }

  /**
   * 切换用户视频（禁视频/解除禁视频）
   * @param {number | string} uid - 用户 ID
   */
  function toggleUserVideo(uid: number | string): void {
    if (isUserVideoMuted(uid)) {
      console.log('aaa==== 解除禁视频');
      unmuteUserVideo(uid);
    } else {
      console.log('aaa==== 禁视频');
      muteUserVideo(uid);
    }
  }

  /**
   * 将某个用户设置为全员主屏（仅创建者）
   * @param {number | string} uid - 用户 ID
   */
  function handleSetMainScreen(uid: number | string): void {
    setMainVideoGlobal(uid);
  }
  /**
   * 踢出用户
   * @param {number | string} uid - 用户 ID
   */
  function handleKickUser(uid: number | string): void {
    kickUser(uid);
  }

  /**
   * 离开会议（仅学生端使用）
   */
  async function handleLeave(): Promise<void> {
    if (isLeaving) {
      return;
    }
    isLeaving = true;
    try {
      // 移除 WebSocket 消息监听
      socket.off('message', onSocketMessage);
      // 离开 WebSocket 房间
      try {
        meetingSocket.leaveRoom(socket);
      } catch {}
      // 销毁 WebSocket 连接
      destroyMeetingSocket();
      // 离开声网会议并清理所有资源
      await cleanup();
      // 返回上一页
      router.back();
    } catch (error) {
      console.error('离开会议失败:', error);
    } finally {
      // eslint-disable-next-line require-atomic-updates
      isLeaving = false;
    }
  }

  /**
   * 组件挂载生命周期钩子
   */
  onMounted(async () => {
    role.value = String(route.query.role) === 'student' ? 'student' : 'teacher';

    try {
      // 使用 Hook 加入 WebSocket 房间获取房间数据
      const roomData = await meetingSocket.joinRoom(socket);
      // 设置房间 ID（用于权限管理中的控制消息发送）
      roomId.value = roomData.roomId;
      // 更新 Hook 内部维护的短 UID（会自动同步到 localUser.uid）
      shortUid.value = roomData.shortUid;

      // 然后初始化声网会议（使用获取到的短 UID）
      classStatus.value = roomData?.classStatus || 'not_started';
      if (classStatus.value === 'in_class') {
        await meeting.initAndJoin(roomData);
      } else {
        Toast.info?.('等待老师上课...');
      }
    } catch (error) {
      console.error('加入会议失败:', error);
      const errorMessage = error instanceof Error ? error.message : '加入会议失败';
      Toast.error(errorMessage);
      router.back();
    }
  });

  /**
   * 组件卸载生命周期钩子
   */
  onUnmounted(async () => {
    if (isLeaving) {
      return;
    }
    socket.off('message', onSocketMessage);
    await cleanup();
    meetingSocket.leaveRoom(socket);
    destroyMeetingSocket();
  });
</script>

<style lang="scss" scoped>
  @keyframes spin {
    0% {
      transform: rotate(0deg);
    }

    100% {
      transform: rotate(360deg);
    }
  }

  .animate-spin {
    animation: spin 1s linear infinite;
  }

  .page {
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100vh;
    padding: 15px;
    overflow: hidden;

    .page-content {
      position: relative;
      box-sizing: border-box;
      display: flex;
      flex-direction: column;
      align-items: flex-start;
      justify-content: flex-start;
      width: 100%;
      height: 100%;
      overflow: hidden;
      background: rgba(255, 255, 255, 0.4);
      border: 1px solid rgba(255, 255, 255, 0.2);
      border-radius: 20px;
      box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.1);
    }
  }

  .top-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 16px 24px;

    .room-info {
      display: flex;
      gap: 12px;
      align-items: center;

      .room-icon {
        width: 24px;
        height: 24px;
        color: #667eea;
      }

      .room-details {
        display: flex;
        flex-direction: column;
        gap: 4px;

        .room-name {
          font-size: 16px;
          font-weight: 600;
          color: #000000;
        }

        .room-id {
          font-size: 12px;
          color: #666666;
        }
      }
    }

    .network-status {
      display: flex;
      gap: 8px;
      align-items: center;
      padding: 8px 16px;
      background: rgba(255, 255, 255, 0.1);
      border-radius: 20px;

      .network-icon {
        width: 18px;
        height: 18px;
      }

      .network-text {
        font-size: 13px;
        font-weight: 500;
      }

      &.good {
        color: #10b981;
        background: rgba(16, 185, 129, 0.1);

        .network-icon {
          color: #10b981;
        }
      }

      &.fair {
        color: #f59e0b;
        background: rgba(245, 158, 11, 0.1);

        .network-icon {
          color: #f59e0b;
        }
      }

      &.poor {
        color: #ef4444;
        background: rgba(239, 68, 68, 0.1);

        .network-icon {
          color: #ef4444;
        }
      }
    }

    .host-badge {
      padding: 4px 12px;
      font-size: 12px;
      font-weight: 600;
      color: #ffffff;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      border-radius: 12px;
    }
  }

  .video-container {
    position: relative;
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: center;
    width: 100%;
    overflow: hidden;

    .video-scrollbar {
      width: 100%;
      height: 100%;
    }

    .remote-videos {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(var(--grid-min-size, 320px), 1fr));
      grid-auto-rows: minmax(180px, auto);
      grid-auto-flow: row dense;
      gap: 16px;
      align-content: start;
      width: 100%;
      min-width: 320px;

      > .video-item:first-child {
        position: relative;
        grid-row: span 2;
        grid-column: span 2;
        width: 100%;
        height: 100%;
        overflow: hidden;
        background: rgba(255, 255, 255, 0.05);
        border-radius: 12px;
        aspect-ratio: 16 / 9;
      }

      .video-item {
        position: relative;
        width: 100%;
        height: 100%;
        overflow: hidden;
        cursor: pointer;
        background: rgba(255, 255, 255, 0.05);
        border-radius: 12px;
        transition: all 0.3s ease;
        aspect-ratio: 16 / 9;

        &:hover {
          z-index: 1;
          transform: translateY(-3px);
        }

        &.is-main-video {
          box-shadow: 0 0 0 3px #667eea;
        }

        .player-wrapper {
          width: 100%;
          height: 100%;
          background-color: #000000;

          :deep(video) {
            width: 100%;
            height: 100%;
            object-fit: cover;
          }
        }

        .user-label {
          position: absolute;
          bottom: 12px;
          left: 12px;
          display: flex;
          flex-wrap: wrap;
          gap: 8px;
          align-items: center;
          padding: 6px 12px;
          font-size: 14px;
          color: #ffffff;
          background: rgba(0, 0, 0, 0.6);
          border-radius: 8px;

          .local-icon {
            width: 16px;
            height: 16px;
          }

          .stream-type-badge {
            padding: 2px 6px;
            font-size: 10px;
            background: rgba(102, 126, 234, 0.8);
            border-radius: 4px;
          }

          .screen-sharing-badge {
            display: flex;
            gap: 4px;
            align-items: center;
            padding: 2px 8px;
            font-size: 11px;
            font-weight: 500;
            color: #ffffff;
            background: rgba(16, 185, 129, 0.8);
            border-radius: 4px;

            .screen-sharing-icon {
              width: 14px;
              height: 14px;
            }
          }
        }

        .user-controls {
          position: absolute;
          top: 8px;
          right: 8px;
          display: flex;
          gap: 8px;
          opacity: 1;
          transition: opacity 0.3s ease;

          .control-icon-btn {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 32px;
            height: 32px;
            padding: 0;
            color: #ffffff;
            cursor: pointer;
            background: rgba(0, 0, 0, 0.6);
            border: none;
            border-radius: 8px;
            transition: all 0.3s ease;

            svg {
              width: 18px;
              height: 18px;
            }

            &:hover {
              background: rgba(0, 0, 0, 0.8);
            }

            &.muted {
              color: #ef4444;
              background: rgba(239, 68, 68, 0.2);
            }

            &.kick {
              color: #ef4444;

              &:hover {
                background: rgba(239, 68, 68, 0.3);
              }
            }

            &.primary {
              color: #667eea;
              background: rgba(102, 126, 234, 0.2);

              &:hover {
                background: rgba(102, 126, 234, 0.3);
              }

              &.active {
                color: #ffffff;
                background: rgba(102, 126, 234, 0.6);
              }
            }
          }
        }

        &.is-local .player-wrapper :deep(video) {
          transform: scaleX(-1);
        }
      }
    }
  }

  .control-bar {
    position: absolute;
    bottom: 13px;
    left: 50%;
    display: flex;
    gap: 20px;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(255, 255, 255, 0.5);
    border-radius: 12px;
    transform: translateX(-50%);

    .control-btn {
      display: flex;
      flex-direction: column;
      gap: 2px;
      align-items: center;
      padding: 10px 16px;
      color: var(--el-color-primary);
      cursor: pointer;
      background: rgba(255, 255, 255, 0.1);
      border: 1px solid var(--el-color-primary-light-7);
      border-radius: 12px;
      transition: all 0.3s ease;

      svg {
        width: 24px;
        height: 24px;
      }

      .btn-text {
        font-size: 13px;
        color: var(--el-color-primary);
      }

      &:disabled {
        cursor: not-allowed;
        opacity: 0.6;
        transform: none !important;
      }

      &:not(:disabled):hover {
        background: rgba(255, 255, 255, 0.2);
        transform: translateY(-2px);
      }

      &.active {
        color: var(--el-color-primary);
        background: var(--el-color-primary-light-9);
      }

      &.danger {
        color: var(--el-color-danger);
        background: var(--el-color-danger-light-9);
        border: 1px solid var(--el-color-danger-light-7);

        .btn-text {
          color: var(--el-color-danger);
        }

        &:hover {
          background: var(--el-color-danger-light-7);
        }
      }
    }
  }
</style>
