<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>
      <div ref="stageRef" class="stage" @pointermove="handleSelfPointerMove" @pointerup="handleSelfPointerUp" @pointercancel="handleSelfPointerUp">
        <el-empty v-if="classStatus !== 'in_class'" :description="classStatus === 'not_started' ? '等待老师上课...' : '老师已下课，等待重新上课...'"></el-empty>
        <template v-else>
          <div class="primary-video">
            <div v-if="primaryUid" :id="`player-${primaryUid}`" class="player-wrapper"></div>
            <div v-else class="primary-empty">暂无主讲画面</div>
            <div class="primary-label video-label">
              <span>{{ primaryName }}</span>
              <span v-if="isPrimaryScreenSharing" class="badge">投屏中</span>
            </div>
          </div>
          <div
            ref="selfWindowRef"
            class="self-window"
            :style="selfWindowStyle"
            @pointerdown.prevent="handleSelfPointerDown"
            @click.stop="handleSelfWindowClick"
          >
            <div v-if="selfWindowUid" :id="`player-${selfWindowUid}`" class="player-wrapper"></div>
            <div v-else id="player-local" class="player-wrapper"></div>
            <div class="self-label video-label">
              <icon-mdi-account class="local-icon"></icon-mdi-account>
              <span>{{ selfWindowName }}</span>
              <span v-if="isTeacherDualStream" class="badge">点击切换</span>
            </div>
          </div>
        </template>
      </div>
      <div class="control-bar">
        <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 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>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { playPromptTone } from '@/utils/prompt-tone';
  import type { IAgoraRTCRemoteUser } from 'agora-rtc-sdk-ng';
  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 IconMdiWifi from '~icons/mdi/wifi';
  import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
  import { useRoute, useRouter } from 'vue-router';
  import { ElMessage as Toast } from 'element-plus';
  import type { OnJoinRoomData, ServerToClientEvents } from './types';
  import { useAgoraMeeting } from './useAgoraMeeting';
  import { destroyMeetingSocket, useMeetingSocket, useWebSocket } from './websocket';
  import { useStreamType } from './useStreamType';

  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 || '用户');
  /** 预计时长 */
  /** 作业 ID */
  const homeworkId = computed(() => Number(route.query.homeworkId || 0));
  /** 课程房间ID */
  const courseRoomId = computed(() => `n_${homeworkId.value}`);

  /** 课堂状态：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>('');
  /** 老师短 UID */
  const teacherUid = ref<number>(0);
  /** 主讲短 UID */
  const speakerUid = ref<number | string | null>(null);
  /** 投屏用户 ID */
  const screenShareStreamUid = ref<number | string | null>(null);
  const screenShareOwnerUid = ref<number | string | null>(null);
  const studentPrimaryOverrideUid = ref<number | string | 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('已离开会议');
      },
    },
    { autoPlayRemoteVideo: false }
  );

  /** 加入方式 */
  const joinMode = computed(() => 'student' as const);
  /** 房间结束时间（毫秒时间戳） */
  const endTimestamp = computed(() => {
    const t = Number(route.query.endTimestamp || 0);
    return Number.isFinite(t) && t > 0 ? t : null;
  });

  const socket = useWebSocket();
  const meetingSocket = useMeetingSocket(courseRoomId, longUserId, userName, homeworkId, joinMode, endTimestamp);
  const streamType = useStreamType(meeting.client);

  const { isMuted, isVideoOff, muteLoading, videoLoading, leaveLoading, networkQuality, selectedUserUid, toggleMute, toggleVideo, leaveChannel, cleanup } =
    meeting;

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

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

  const isTeacherDualStream = computed(() => Boolean(teacherUid.value && screenShareStreamUid.value));

  const teacherPreferredPrimaryUid = computed<number | string | null>(() => {
    return screenShareStreamUid.value ?? speakerUid.value ?? (teacherUid.value || null);
  });

  const primaryUid = computed<number | string | null>(() => {
    if (isTeacherDualStream.value && studentPrimaryOverrideUid.value) {
      const candidate = studentPrimaryOverrideUid.value;
      if (candidate === teacherUid.value || candidate === screenShareStreamUid.value) {
        return candidate;
      }
    }
    return teacherPreferredPrimaryUid.value;
  });

  const secondaryUid = computed<number | string | null>(() => {
    if (!isTeacherDualStream.value) {
      return null;
    }
    if (!teacherUid.value || !screenShareStreamUid.value) {
      return null;
    }
    return primaryUid.value === screenShareStreamUid.value ? teacherUid.value : screenShareStreamUid.value;
  });

  const selfWindowUid = computed<number | string | null>(() => secondaryUid.value);

  const screenOwnerName = computed(() => {
    const uid = screenShareOwnerUid.value;
    if (!uid) {
      return '';
    }
    const u = meeting.remoteUsers.value.find((it: IAgoraRTCRemoteUser) => it.uid === uid) as (IAgoraRTCRemoteUser & { userName?: string }) | undefined;
    return u?.userName || (uid === meeting.localUser.uid ? userName.value : `用户 ${uid}`);
  });

  const selfWindowName = computed(() => {
    if (!selfWindowUid.value) {
      return userName.value;
    }
    if (selfWindowUid.value === screenShareStreamUid.value) {
      return `${screenOwnerName.value || '主讲'}（投屏）`;
    }
    return '主讲（摄像头）';
  });

  /** 主讲名称 */
  const primaryName = computed(() => {
    const uid = primaryUid.value;
    if (!uid) {
      return '主讲';
    }
    if (screenShareStreamUid.value && uid === screenShareStreamUid.value) {
      return `${screenOwnerName.value || '主讲'}（投屏）`;
    }
    const u = meeting.remoteUsers.value.find((it: IAgoraRTCRemoteUser) => it.uid === uid) as (IAgoraRTCRemoteUser & { userName?: string }) | undefined;
    return u?.userName || (uid === meeting.localUser.uid ? userName.value : `用户 ${uid}`);
  });

  /** 是否为投屏主画面 */
  const isPrimaryScreenSharing = computed(() => Boolean(primaryUid.value && screenShareStreamUid.value === primaryUid.value));

  /** 舞台容器 DOM */
  const stageRef = ref<HTMLDivElement | null>(null);
  /** 小窗容器 DOM */
  const selfWindowRef = ref<HTMLDivElement | null>(null);

  /** 小窗位置 X（单位：px） */
  const selfX = ref<number>(0);
  /** 小窗位置 Y（单位：px） */
  const selfY = ref<number>(0);
  /** 是否已经人为拖动过 */
  const hasMoved = ref<boolean>(false);
  /** 是否正在拖动 */
  const isDragging = ref<boolean>(false);
  /** 拖动偏移 X */
  const dragOffsetX = ref<number>(0);
  /** 拖动偏移 Y */
  const dragOffsetY = ref<number>(0);

  /** 小窗样式 */
  const selfWindowStyle = computed(() => ({
    transform: `translate3d(${selfX.value}px, ${selfY.value}px, 0)`,
  }));

  /**
   * 计算并设置小窗默认位置（右下角）
   */
  function initSelfWindowPosition() {
    if (hasMoved.value) {
      return;
    }
    const stageEl = stageRef.value;
    const selfEl = selfWindowRef.value;
    if (!stageEl || !selfEl) {
      return;
    }
    const stageRect = stageEl.getBoundingClientRect();
    const selfRect = selfEl.getBoundingClientRect();
    const padding = 16;
    selfX.value = Math.max(0, Math.floor(stageRect.width - selfRect.width - padding));
    selfY.value = Math.max(0, Math.floor(stageRect.height - selfRect.height - padding));
  }

  /**
   * 小窗按下开始拖动
   * @param e - 指针事件
   */
  function handleSelfPointerDown(e: PointerEvent) {
    const stageEl = stageRef.value;
    const selfEl = selfWindowRef.value;
    if (!stageEl || !selfEl) {
      return;
    }
    hasMoved.value = true;
    isDragging.value = true;
    try {
      selfEl.setPointerCapture(e.pointerId);
    } catch {}
    const rect = selfEl.getBoundingClientRect();
    dragOffsetX.value = e.clientX - rect.left;
    dragOffsetY.value = e.clientY - rect.top;
  }

  /**
   * 小窗拖动移动
   * @param e - 指针事件
   */
  function handleSelfPointerMove(e: PointerEvent) {
    if (!isDragging.value) {
      return;
    }
    const stageEl = stageRef.value;
    const selfEl = selfWindowRef.value;
    if (!stageEl || !selfEl) {
      return;
    }
    const stageRect = stageEl.getBoundingClientRect();
    const selfRect = selfEl.getBoundingClientRect();
    const nextX = e.clientX - stageRect.left - dragOffsetX.value;
    const nextY = e.clientY - stageRect.top - dragOffsetY.value;
    const maxX = Math.max(0, stageRect.width - selfRect.width);
    const maxY = Math.max(0, stageRect.height - selfRect.height);
    selfX.value = Math.max(0, Math.min(Math.floor(nextX), Math.floor(maxX)));
    selfY.value = Math.max(0, Math.min(Math.floor(nextY), Math.floor(maxY)));
  }

  /**
   * 小窗抬起结束拖动
   * @param e - 指针事件
   */
  function handleSelfPointerUp(e: PointerEvent) {
    if (!isDragging.value) {
      return;
    }
    isDragging.value = false;
    const selfEl = selfWindowRef.value;
    if (!selfEl) {
      return;
    }
    try {
      selfEl.releasePointerCapture(e.pointerId);
    } catch {}
  }

  /**
   * 确保主讲视频能播放到当前主讲容器
   * @param uid - 主讲 UID
   */
  async function ensurePrimaryVideoPlaying(uid: number | string | null) {
    if (!uid) {
      return;
    }
    await nextTick();
    const el = document.getElementById(`player-${uid}`);
    if (!el) {
      return;
    }
    const client = meeting.client.value;
    const user = client?.remoteUsers?.find((u: IAgoraRTCRemoteUser) => u.uid === uid);
    if (user?.videoTrack) {
      user.videoTrack.play(el);
    }
  }

  const ensureLocalVideoPlaying = async () => {
    await nextTick();
    const el = document.getElementById('player-local');
    if (!el) {
      return;
    }
    if (el.querySelector('video')) {
      return;
    }
    const track = meeting.localUser.videoTrack;
    if (track) {
      track.play(el);
    }
  };

  watch(
    () => primaryUid.value,
    (uid) => {
      ensurePrimaryVideoPlaying(uid);
      if (uid) {
        selectedUserUid.value = uid;
      }
    },
    { immediate: true }
  );

  watch(
    () => selfWindowUid.value,
    (uid) => {
      if (!uid) {
        ensureLocalVideoPlaying();
      } else {
        ensurePrimaryVideoPlaying(uid);
      }
    },
    { immediate: true }
  );

  watch(
    () => meeting.remoteUsers.value.length,
    () => {
      ensurePrimaryVideoPlaying(primaryUid.value);
      ensurePrimaryVideoPlaying(secondaryUid.value);
    }
  );

  watch(
    () => secondaryUid.value,
    (uid) => {
      ensurePrimaryVideoPlaying(uid);
    }
  );

  watch(
    () => teacherPreferredPrimaryUid.value,
    () => {
      studentPrimaryOverrideUid.value = null;
    }
  );

  watch(
    () => isTeacherDualStream.value,
    (enabled) => {
      if (!enabled) {
        studentPrimaryOverrideUid.value = null;
      }
    }
  );

  const handleSelfWindowClick = () => {
    if (!isTeacherDualStream.value) {
      return;
    }
    if (isDragging.value) {
      return;
    }
    const uid = selfWindowUid.value;
    if (!uid) {
      return;
    }
    studentPrimaryOverrideUid.value = uid;
    ensurePrimaryVideoPlaying(uid);
  };

  const isLeaving = ref<boolean>(false);
  type MeetingWsPacket = Parameters<ServerToClientEvents['message']>[0];

  /**
   * 离开会议
   */
  async function handleLeave(): Promise<void> {
    if (isLeaving.value) {
      return;
    }
    isLeaving.value = true;
    try {
      // ✅ 移除所有事件监听器
      socket.off('message', onSocketMessage);

      // ✅ 移除声网客户端的事件监听器
      if (meeting.client.value) {
        meeting.client.value.removeAllListeners?.();
      }

      try {
        meetingSocket.leaveRoom(socket);
      } catch {}
      destroyMeetingSocket();
      await cleanup();
      router.back();
    } catch (error) {
      console.error('离开会议失败:', error);
    } finally {
      isLeaving.value = false;
    }
  }

  /**
   * WebSocket 消息处理
   * @param packet - 消息包
   */
  function 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?.teacherUid === 'number') {
          teacherUid.value = data.teacherUid;
        }
        if (typeof data?.speakerUid === 'number') {
          speakerUid.value = data.speakerUid;
          selectedUserUid.value = data.speakerUid;
          streamType.setMainVideo(data.speakerUid);
        }
        if (typeof data?.screenShareUid === 'number') {
          screenShareStreamUid.value = data.screenShareUid;
        }
        if (typeof data?.screenShareOwnerUid === 'number') {
          screenShareOwnerUid.value = data.screenShareOwnerUid;
        }
        break;
      }
      case 'sev_mute_audio': {
        meeting.forceMuteAudio();
        Toast.warning('您已被创建者禁麦');
        break;
      }
      case 'sev_unmute_audio': {
        meeting.forceUnmuteAudio();
        Toast.success('您已被解除禁麦');
        break;
      }
      case 'sev_mute_video': {
        meeting.forceMuteVideo();
        Toast.warning('您已被创建者禁视频');
        break;
      }
      case 'sev_unmute_video': {
        meeting.forceUnmuteVideo();
        Toast.success('您已被解除禁视频');
        break;
      }
      case 'sev_set_main_video': {
        const targetUid = packet.data.targetUid;
        speakerUid.value = 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;
        if (typeof targetUid === 'number') {
          screenShareOwnerUid.value = targetUid;
        }
        if (typeof screenUid === 'number') {
          screenShareStreamUid.value = screenUid;
          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;
            teacherUid.value = roomData.teacherUid;
            speakerUid.value = roomData.speakerUid;
            selectedUserUid.value = roomData.speakerUid;
            streamType.setMainVideo(roomData.speakerUid);
            if (typeof roomData.screenShareUid === 'number') {
              screenShareStreamUid.value = roomData.screenShareUid;
            }
            if (typeof roomData.screenShareOwnerUid === 'number') {
              screenShareOwnerUid.value = roomData.screenShareOwnerUid;
            }
            await meeting.initAndJoin(roomData);
            // ✅ 加入后先检查是否有已存在的远程用户
            const existingUsers = meeting.client.value?.remoteUsers || [];
            console.log(
              '[学生端] 加入会议，已存在的远程用户:',
              existingUsers.map((u) => u.uid)
            );

            // 等待 DOM 更新后再尝试播放视频
            await nextTick();
            setTimeout(() => {
              ensurePrimaryVideoPlaying(primaryUid.value);
              ensurePrimaryVideoPlaying(secondaryUid.value);
              // 如果有已存在的用户，也检查一下
              existingUsers.forEach((user) => {
                ensurePrimaryVideoPlaying(user.uid);
              });
            }, 500);
            initSelfWindowPosition();
          })
          .catch((e: unknown) => {
            if (e instanceof Error && e.message) {
              Toast.error(e.message);
              return;
            }
            Toast.error('加入失败');
          });
        break;
      }
      case 'sev_class_ended': {
        classStatus.value = 'finished';
        screenShareOwnerUid.value = null;
        screenShareStreamUid.value = null;
        leaveChannel().catch(() => {});
        Toast.info?.('老师已下课，等待重新上课');
        break;
      }
      case 'sev_kick_user': {
        Toast.error('您已被创建者移出会议');
        handleLeave();
        break;
      }
      case 'sev_device_conflict': {
        playPromptTone();
        Toast.warning('您已在其他设备进入课程');
        handleLeave();
        break;
      }
      default:
        console.log('未处理的消息====', packet);
        break;
    }
  }

  socket.on('message', onSocketMessage);

  /**
   * 页面挂载后加入房间
   */
  onMounted(async () => {
    try {
      const roomData = await meetingSocket.joinRoom(socket);
      roomId.value = roomData.roomId;
      shortUid.value = roomData.shortUid;
      teacherUid.value = roomData.teacherUid;
      speakerUid.value = roomData.speakerUid;
      selectedUserUid.value = roomData.speakerUid;
      streamType.setMainVideo(roomData.speakerUid);
      classStatus.value = roomData?.classStatus || 'not_started';
      if (roomData.screenShareUid) {
        screenShareStreamUid.value = roomData.screenShareUid;
      }
      if (typeof roomData.screenShareOwnerUid === 'number') {
        screenShareOwnerUid.value = roomData.screenShareOwnerUid;
      }
      if (classStatus.value === 'in_class') {
        await meeting.initAndJoin(roomData);
        // ✅ 加入后先检查是否有已存在的远程用户
        const existingUsers = meeting.client.value?.remoteUsers || [];
        console.log(
          '[学生端] 加入会议，已存在的远程用户:',
          existingUsers.map((u) => u.uid)
        );

        // 等待 DOM 更新后再尝试播放视频
        await nextTick();
        setTimeout(() => {
          ensurePrimaryVideoPlaying(primaryUid.value);
          ensurePrimaryVideoPlaying(secondaryUid.value);
          // 如果有已存在的用户，也检查一下
          existingUsers.forEach((user) => {
            ensurePrimaryVideoPlaying(user.uid);
          });
        }, 500);
        initSelfWindowPosition();
      } else {
        Toast.info?.('等待老师上课...');
      }
      setTimeout(() => {
        initSelfWindowPosition();
      }, 100);
    } catch (error) {
      console.error('加入会议失败:', error);
      const errorMessage = error instanceof Error ? error.message : '加入会议失败';
      Toast.error(errorMessage);
      router.back();
    }
  });

  /**
   * 页面卸载清理
   */
  onUnmounted(async () => {
    if (isLeaving.value) {
      return;
    }
    // ✅ 移除所有事件监听器
    socket.off('message', onSocketMessage);

    // ✅ 移除声网客户端的事件监听器
    if (meeting.client.value) {
      meeting.client.value.removeAllListeners?.();
    }

    await cleanup();
    try {
      meetingSocket.leaveRoom(socket);
    } catch {}
    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;
      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;

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

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

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

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

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

  .stage {
    position: relative;
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 15px;
    padding-top: 0;
    overflow: hidden;
  }

  .primary-video {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.82);
    border-radius: 16px;
  }

  .primary-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    font-size: 14px;
    color: rgba(255, 255, 255, 0.8);
  }

  .player-wrapper {
    width: 100%;
    height: 100%;
  }

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

  .badge {
    padding: 2px 8px;
    font-size: 11px;
    background: rgba(16, 185, 129, 0.8);
    border-radius: 8px;
  }

  .self-window {
    position: absolute;
    top: 0;
    left: 0;
    width: 240px;
    height: 160px;
    overflow: hidden;
    cursor: grab;
    background: rgba(0, 0, 0, 0.85);
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 14px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);

    &:active {
      cursor: grabbing;
    }
  }

  .self-label {
    top: 8px;
    bottom: auto;
  }

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

  .control-bar {
    position: absolute;
    bottom: 13px;
    left: 50%;
    z-index: 10;
    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);
      }
    }
  }

  .player-wrapper :deep(video) {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
</style>
