<script setup lang="ts">
// 화상회의 — 상태 머신(로비 → 프리조인 → 연결 중 → 회의실).
// 라이브 상태는 useLiveKitRoom 이 보유하고, 화면 조각은 components/meeting/* 가 담당한다.
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import AppShell from '@/layouts/AppShell.vue'
import MeetingLobby from '@/components/meeting/MeetingLobby.vue'
import MeetingPrejoin from '@/components/meeting/MeetingPrejoin.vue'
import MeetingRoom from '@/components/meeting/MeetingRoom.vue'
import { useMeeting, type MeetingRole } from '@/composables/useMeeting'
import { useLiveKitRoom } from '@/composables/useLiveKitRoom'
import { useDialog } from '@/composables/useDialog'
import { useAuthStore } from '@/stores/auth.store'
import type { ApiMeetingRoom } from '@/types/meeting'

const route = useRoute()
const router = useRouter()
const meetingApi = useMeeting()
const dialog = useDialog()
const authStore = useAuthStore()

const {
  participants,
  connected,
  connecting,
  micEnabled,
  camEnabled,
  featuredIdentity,
  messages,
  connect,
  disconnect,
  toggleMic,
  toggleCam,
  promote,
  sendChat,
} = useLiveKitRoom()

// 프리조인 대상 방 정보(표시용 + 입장 코드)
interface PrejoinTarget {
  code: string
  name: string
  hostName: string | null
  restricted: boolean
  isHost: boolean
}

const phase = ref<'lobby' | 'prejoin'>('lobby')
const entering = ref(false) // 토큰 발급~연결 시작 사이 깜빡임 방지
const target = ref<PrejoinTarget | null>(null)
const joinError = ref<string | null>(null)
const myRole = ref<MeetingRole | null>(null)
const rooms = ref<ApiMeetingRoom[]>([])

const me = computed(() => authStore.user)
const myName = computed(() => me.value?.name || '나')
const myAvatarUrl = computed(() => me.value?.avatarUrl ?? null)
const localId = computed(() => participants.value[0]?.identity ?? null)
const isHost = computed(() => myRole.value === 'host')

// 현재 보여줄 화면 결정
const screen = computed<'lobby' | 'prejoin' | 'connecting' | 'room'>(() => {
  if (connected.value) return 'room'
  if (entering.value || connecting.value) return 'connecting'
  if (phase.value === 'prejoin' && target.value) return 'prejoin'
  return 'lobby'
})

async function loadRooms() {
  try {
    rooms.value = await meetingApi.listRooms()
  } catch {
    // 목록 조회 실패는 인터셉터에서 전역 처리
  }
}

// 로비에서 방 선택 → 프리조인으로 (방은 명시적으로 생성된 것만 입장 가능)
function onJoin(room: ApiMeetingRoom) {
  if (room.restricted && !room.canJoin) {
    joinError.value = `'${room.name}' 방은 참여가 제한되어 있습니다. 진행자에게 허용을 요청하세요.`
    return
  }
  joinError.value = null
  target.value = {
    code: room.code,
    name: room.name,
    hostName: room.host?.name ?? null,
    restricted: room.restricted,
    isHost: room.isHost,
  }
  phase.value = 'prejoin'
}

// 프리조인 → 입장(토큰 발급 + 연결)
async function onEnter(opts: { mic: boolean; cam: boolean }) {
  const t = target.value
  if (!t) return
  entering.value = true
  try {
    const { token, role } = await meetingApi.getToken(t.code)
    myRole.value = role
    await connect(token, opts)
    if (route.params.code !== t.code) {
      void router.replace(`/meeting/${t.code}`)
    }
  } catch {
    joinError.value = '입장에 실패했습니다. 잠시 후 다시 시도해 주세요.'
    phase.value = 'lobby'
    target.value = null
  } finally {
    entering.value = false
  }
}

// 프리조인 취소 → 로비
function onCancelPrejoin() {
  phase.value = 'lobby'
  target.value = null
}

// 나가기 → 로비
async function onLeave() {
  await disconnect()
  myRole.value = null
  phase.value = 'lobby'
  target.value = null
  void router.replace('/meeting')
  void loadRooms()
}

// 방 삭제(진행자만)
async function onDelete(r: ApiMeetingRoom) {
  const ok = await dialog.confirm(
    `'${r.name}' 방을 삭제할까요? 이 작업은 되돌릴 수 없습니다.`,
    { title: '방 삭제', confirmText: '삭제', variant: 'danger' },
  )
  if (!ok) return
  try {
    await meetingApi.removeRoom(r.id)
    await loadRooms()
  } catch {
    // 전역 처리
  }
}

// 방 생성 성공 → 목록 갱신
function onCreated() {
  void loadRooms()
}

// 진입 시 방 목록 로드. 공유 링크(/meeting/:room)면 그 방 프리조인으로 바로 이동.
onMounted(async () => {
  await loadRooms()
  // 공유/초대 링크(/meeting/:code) — 코드로 방을 찾아 프리조인. 없으면 안내한다.
  const param = route.params.code
  if (typeof param === 'string' && param) {
    const room = rooms.value.find((r) => r.code === param)
    if (room) onJoin(room)
    else joinError.value = '회의 방을 찾을 수 없습니다. 링크를 확인해 주세요.'
  }
})

// 화면 이탈 시 연결 정리
onBeforeUnmount(() => {
  void disconnect()
})
</script>

<template>
  <AppShell>
    <!-- 로비(라이트) -->
    <MeetingLobby
      v-if="screen === 'lobby'"
      :rooms="rooms"
      :join-error="joinError"
      @join="onJoin"
      @created="onCreated"
      @delete="onDelete"
      @clear-error="joinError = null"
    />

    <!-- 다크 캔버스(프리조인 / 연결 중 / 회의실) -->
    <div
      v-else
      class="meeting-canvas"
    >
      <MeetingPrejoin
        v-if="screen === 'prejoin' && target"
        :room="target"
        :my-name="myName"
        :my-avatar-url="myAvatarUrl"
        @enter="onEnter"
        @cancel="onCancelPrejoin"
      />

      <div
        v-else-if="screen === 'connecting'"
        class="connecting"
      >
        <div class="cn-spin" />
        <div class="cn-name">
          {{ target?.name }}
        </div>
        <div class="cn-txt">
          회의에 연결하는 중…
        </div>
      </div>

      <MeetingRoom
        v-else-if="screen === 'room'"
        :room-name="target?.name ?? ''"
        :participants="participants"
        :featured-identity="featuredIdentity"
        :local-id="localId"
        :mic-enabled="micEnabled"
        :cam-enabled="camEnabled"
        :is-host="isHost"
        :messages="messages"
        @toggle-mic="toggleMic"
        @toggle-cam="toggleCam"
        @promote="promote"
        @send="sendChat"
        @leave="onLeave"
      />
    </div>
  </AppShell>
</template>

<style scoped>
/* 다크 캔버스 — 사이드바/상단바(3.25rem) 아래 영역을 가득 채운다 */
.meeting-canvas {
  height: calc(100vh - 3.25rem);
  background: var(--d-bg);
  overflow: hidden;
}

/* 연결 중 */
.connecting {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
}
.cn-spin {
  width: 2.875rem;
  height: 2.875rem;
  border-radius: 50%;
  border: 0.1875rem solid var(--d-border);
  border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
.cn-name {
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--d-text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.cn-txt {
  font-size: 0.84375rem;
  color: var(--d-text-3);
}

@media (max-width: 48rem) {
  .meeting-canvas {
    height: calc(100vh - 3.25rem);
  }
}
</style>
