<template>
    <el-dialog
        v-model="visible"
        :title="dialogTitle"
        width="760px"
        destroy-on-close
        append-to-body
        :close-on-click-modal="false"
        class="assistant-watch-call-dialog"
        @closed="onClosed"
    >
        <div v-if="loading" class="watch-state">正在连接房间…</div>
        <div v-else-if="errorMsg" class="watch-state watch-error">{{ errorMsg }}</div>
        <div v-show="!loading && !errorMsg" ref="gridRef" class="watch-grid" />
        <template #footer>
            <span class="watch-hint">仅观看，不会开启摄像头与麦克风</span>
            <el-button type="primary" @click="visible = false">离开</el-button>
        </template>
    </el-dialog>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import TRTC from 'trtc-sdk-v5'
import type { TRTCStreamType } from 'trtc-sdk-v5'
import { getAssistantWatchCallParams } from '@/api/tcm'

const props = defineProps<{
    modelValue: boolean
    diagnosisId: number
}>()

const emit = defineEmits<{
    'update:modelValue': [boolean]
    closed: []
}>()

const visible = computed({
    get: () => props.modelValue,
    set: (v: boolean) => emit('update:modelValue', v)
})

const gridRef = ref<HTMLElement | null>(null)
const loading = ref(false)
const errorMsg = ref('')
const dialogTitle = ref('旁观视频通话')

type TileEntry = { wrap: HTMLElement; userId: string; streamType: TRTCStreamType }
const tiles = new Map<string, TileEntry>()

let trtc: ReturnType<typeof TRTC.create> | null = null
let joinSession = 0

function tileKey(userId: string, streamType: TRTCStreamType) {
    return `${userId}\u0000${String(streamType)}`
}

function remoteLabel(userId: string) {
    if (userId.startsWith('patient_')) return '患者'
    if (userId.startsWith('doctor_')) return '医护'
    return userId
}

async function onRemoteVideoAvailable(event: { userId: string; streamType: TRTCStreamType }) {
    if (!trtc || !gridRef.value) return
    if (event.streamType !== TRTC.TYPE.STREAM_TYPE_MAIN) return

    const key = tileKey(event.userId, event.streamType)
    if (tiles.has(key)) return

    const wrap = document.createElement('div')
    wrap.className = 'watch-tile'
    const cap = document.createElement('div')
    cap.className = 'watch-tile-cap'
    cap.textContent = remoteLabel(event.userId)
    const view = document.createElement('div')
    view.className = 'watch-tile-view'
    wrap.appendChild(cap)
    wrap.appendChild(view)
    gridRef.value.appendChild(wrap)
    tiles.set(key, { wrap, userId: event.userId, streamType: event.streamType })

    try {
        await trtc.startRemoteVideo({
            userId: event.userId,
            streamType: event.streamType,
            view
        })
    } catch (e) {
        console.warn('[AssistantWatchCall] startRemoteVideo', e)
    }
}

async function onRemoteVideoUnavailable(event: { userId: string; streamType: TRTCStreamType }) {
    if (!trtc) return
    const key = tileKey(event.userId, event.streamType)
    const t = tiles.get(key)
    if (!t) return
    try {
        await trtc.stopRemoteVideo({ userId: event.userId, streamType: event.streamType })
    } catch {
        /* ignore */
    }
    t.wrap.remove()
    tiles.delete(key)
}

function bindTrtcEvents() {
    if (!trtc) return
    trtc.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, onRemoteVideoAvailable)
    trtc.on(TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE, onRemoteVideoUnavailable)
}

function unbindTrtcEvents() {
    if (!trtc) return
    trtc.off(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, onRemoteVideoAvailable)
    trtc.off(TRTC.EVENT.REMOTE_VIDEO_UNAVAILABLE, onRemoteVideoUnavailable)
}

async function cleanup() {
    unbindTrtcEvents()
    if (trtc) {
        for (const [, t] of tiles) {
            try {
                await trtc.stopRemoteVideo({ userId: t.userId, streamType: t.streamType })
            } catch {
                /* ignore */
            }
            t.wrap.remove()
        }
        tiles.clear()
        if (gridRef.value) gridRef.value.innerHTML = ''
        try {
            await trtc.exitRoom()
        } catch {
            /* ignore */
        }
        try {
            trtc.destroy()
        } catch {
            /* ignore */
        }
        trtc = null
    } else {
        tiles.clear()
        if (gridRef.value) gridRef.value.innerHTML = ''
    }
}

async function joinRoom() {
    const session = ++joinSession
    await cleanup()
    if (!props.diagnosisId) {
        errorMsg.value = '诊单无效'
        return
    }

    loading.value = true
    errorMsg.value = ''
    dialogTitle.value = '旁观视频通话'

    try {
        const data = (await getAssistantWatchCallParams({
            diagnosis_id: props.diagnosisId
        })) as {
            sdkAppId: number
            userId: string
            userSig: string
            roomId?: number
            strRoomId?: string
            patientName?: string
        }

        if (session !== joinSession) {
            return
        }

        if (data.patientName) {
            dialogTitle.value = `旁观视频通话 · ${data.patientName}`
        }

        trtc = TRTC.create()
        bindTrtcEvents()

        const roomCfg = {
            sdkAppId: data.sdkAppId,
            userId: data.userId,
            userSig: data.userSig,
            autoReceiveAudio: true,
            autoReceiveVideo: true,
            ...(data.roomId != null && data.roomId > 0
                ? { roomId: data.roomId }
                : { strRoomId: data.strRoomId as string })
        }
        if (!(data.roomId != null && data.roomId > 0) && !data.strRoomId) {
            throw new Error('缺少房间号')
        }

        await trtc.enterRoom(roomCfg)
        if (session !== joinSession) {
            await cleanup()
            return
        }
        loading.value = false
    } catch (e: unknown) {
        loading.value = false
        let msg = '进入房间失败'
        if (typeof e === 'string') msg = e
        else if (e && typeof e === 'object') {
            const o = e as { msg?: string; message?: string }
            if (o.msg) msg = String(o.msg)
            else if (o.message) msg = String(o.message)
        }
        errorMsg.value = msg
        await cleanup()
    }
}

function onClosed() {
    void cleanup()
    loading.value = false
    errorMsg.value = ''
    dialogTitle.value = '旁观视频通话'
    emit('closed')
}

watch(
    () => [props.modelValue, props.diagnosisId] as const,
    ([open, id]) => {
        if (!open) {
            joinSession++
            void cleanup()
            return
        }
        if (id > 0) {
            void joinRoom()
        }
    }
)
</script>

<style scoped>
.watch-state {
    min-height: 200px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--el-text-color-secondary);
    font-size: 14px;
}
.watch-error {
    color: var(--el-color-danger);
}
.watch-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 12px;
    min-height: 220px;
}
.watch-tile {
    background: #0f0f0f;
    border-radius: 8px;
    overflow: hidden;
    aspect-ratio: 16 / 10;
    display: flex;
    flex-direction: column;
}
.watch-tile-cap {
    padding: 6px 10px;
    font-size: 12px;
    color: #e5e5e5;
    background: rgba(0, 0, 0, 0.55);
}
.watch-tile-view {
    flex: 1;
    min-height: 0;
    position: relative;
}
.watch-hint {
    float: left;
    line-height: 32px;
    font-size: 12px;
    color: var(--el-text-color-secondary);
}
</style>
