<template>
  <view class="cloud-law-call">
    <view v-if="!isReady" class="entry-state">
      <view class="entry-avatar">{{ entryInitial }}</view>
      <text class="entry-title">{{ entryTitle }}</text>
      <text class="entry-desc">{{ joinError || stateText }}</text>
      <button v-if="joinError" class="retry-btn" @click="joinMeeting">重新进入</button>
    </view>

    <view v-else class="call-shell">
      <view class="remote-stage">
        <VideoTile
          v-if="primaryTile"
          class="main-video"
          :user-id="primaryTile.userId"
          :name="primaryTile.name"
          :is-local="primaryTile.isLocal"
          :is-host="primaryTile.isHost"
          :audio-enabled="primaryTile.audioEnabled"
          :video-enabled="primaryTile.videoEnabled"
          :audio-track="primaryTile.audioTrack"
          :video-track="primaryTile.videoTrack"
          :is-speaking="!!speakingMap[primaryTile.userId]"
        />
        <view v-else class="empty-video">
          <view class="entry-avatar">{{ entryInitial }}</view>
          <text class="entry-title">在线律师</text>
          <text class="entry-desc">等待视频画面…</text>
        </view>
      </view>

      <view v-if="selfTile" class="self-preview">
        <VideoTile
          class="self-video"
          :user-id="selfTile.userId"
          :name="selfTile.name"
          :is-local="selfTile.isLocal"
          :is-host="selfTile.isHost"
          :audio-enabled="selfTile.audioEnabled"
          :video-enabled="selfTile.videoEnabled"
          :audio-track="selfTile.audioTrack"
          :video-track="selfTile.videoTrack"
          :is-speaking="!!speakingMap[selfTile.userId]"
          :compact="true"
        />
      </view>

      <view class="top-info">
        <view>
          <text class="call-title">视频咨询</text>
          <text class="call-code">{{ formattedCode }}</text>
        </view>
        <view class="status-pill" :class="stateClass">
          <view class="status-dot"></view>
          <text>{{ stateText }}</text>
        </view>
      </view>

      <view class="timer">{{ durationText }}</view>

      <view class="controls">
        <view class="control-item" @click="onCamToggle">
          <view class="control-btn" :class="{ disabled: !videoEnabled, loading: videoLoading }">
            <text class="control-icon">{{ videoEnabled ? '摄' : '关' }}</text>
          </view>
          <text class="control-label">摄像头{{ videoEnabled ? '已开' : '已关' }}</text>
        </view>

        <view class="control-item" @click="hangup">
          <view class="hangup-btn" :class="{ loading: leaving }">
            <text class="hangup-icon">挂</text>
          </view>
          <text class="control-label">挂断</text>
        </view>

        <view class="control-item" @click="onMicToggle">
          <view class="control-btn" :class="{ disabled: !audioEnabled, loading: audioLoading }">
            <text class="control-icon">{{ audioEnabled ? '麦' : '静' }}</text>
          </view>
          <text class="control-label">麦克风{{ audioEnabled ? '已开' : '已关' }}</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { useMeetingStore } from '@/store/meeting'
import { useUserStore } from '@/store/user'
import {
  MEETING_LOCAL_STATE_CONNECTED,
  MEETING_LOCAL_STATE_CONNECTING,
  MEETING_LOCAL_STATE_JOINING,
  MEETING_LOCAL_STATE_RECONNECTING,
  MEETING_LOCAL_STATE_LEAVING,
  MEETING_LOCAL_STATE_ENDED,
  MEETING_ENDED_REASON_LABEL
} from '@/constants/meeting'
import VideoTile from '@/components/meeting/VideoTile.vue'

const meetingStore = useMeetingStore()
const userStore = useUserStore()

const rawCode = ref('')
const joining = ref(false)
const joinError = ref('')
const audioLoading = ref(false)
const videoLoading = ref(false)
const leaving = ref(false)
const startedAt = ref(0)
const nowTs = ref(Date.now())
const ended = ref(false)
let timerHandle = null
let stateStopWatch = null

const digitsOf = (value) => String(value || '').replace(/\D/g, '').slice(0, 9)
const sameUser = (a, b) => String(a || '') === String(b || '')

const formatCode = (value) => {
  const digits = digitsOf(value)
  if (digits.length !== 9) return String(value || '')
  return `${digits.slice(0, 3)}-${digits.slice(3, 6)}-${digits.slice(6)}`
}

const formattedCode = computed(() => formatCode(rawCode.value))
const myUserId = computed(() => userStore.userInfo?.id || userStore.userInfo?.user_id || '')
const hostId = computed(() => meetingStore.currentRoom?.host_id || '')
const audioEnabled = computed(() => !!meetingStore.localAudioEnabled)
const videoEnabled = computed(() => !!meetingStore.localVideoEnabled)
const speakingMap = computed(() => meetingStore.speakingMap || {})

const entryInitial = computed(() => {
  const name = userStore.userInfo?.nickname || userStore.userInfo?.username || '在'
  return String(name || '在').slice(0, 1)
})

const entryTitle = computed(() => {
  if (joinError.value) return '视频咨询接入失败'
  if (ended.value) return '视频咨询已结束'
  return '正在进入视频咨询'
})

const participants = computed(() => {
  const list = meetingStore.activeParticipants || []
  return list.map(p => {
    if (!sameUser(p.user_id, myUserId.value)) return p
    return {
      ...p,
      audio_enabled: meetingStore.localAudioEnabled,
      video_enabled: meetingStore.localVideoEnabled
    }
  })
})

const localTrackVersion = computed(() => {
  const producers = meetingStore.localProducers || {}
  return `${producers.audio || ''}-${producers.video || ''}`
})

const remoteTrackVersion = computed(() => {
  const parts = []
  const map = meetingStore.remoteConsumers || {}
  Object.keys(map).forEach(uid => {
    const slot = map[uid]
    parts.push(`${uid}:${slot?.audio?.id || ''}:${slot?.video?.id || ''}`)
  })
  return parts.join('|')
})

const displayNameMap = computed(() => {
  const map = {}
  participants.value.forEach(p => {
    if (p.user_name) map[p.user_id] = p.user_name
  })
  const me = userStore.userInfo || {}
  const uid = me.id || me.user_id
  if (uid) map[uid] = me.nickname || me.username || map[uid]
  return map
})

const nameOf = (uid) => {
  if (sameUser(uid, myUserId.value)) return '你'
  return displayNameMap.value[uid] || `用户 ${uid}`
}

const tiles = computed(() => {
  localTrackVersion.value
  remoteTrackVersion.value

  const list = []
  participants.value.forEach(p => {
    if (!p.is_active && p.is_active !== undefined) return
    const isLocal = sameUser(p.user_id, myUserId.value)
    const audioTrack = isLocal ? meetingStore.getLocalTrack('audio') : meetingStore.getRemoteTrack(p.user_id, 'audio')
    const videoTrack = isLocal ? meetingStore.getLocalTrack('video') : meetingStore.getRemoteTrack(p.user_id, 'video')
    list.push({
      key: `u_${p.user_id}`,
      userId: p.user_id,
      name: nameOf(p.user_id),
      isLocal,
      isHost: sameUser(p.user_id, hostId.value),
      audioEnabled: isLocal ? audioEnabled.value : !!audioTrack,
      videoEnabled: isLocal ? videoEnabled.value : !!videoTrack,
      audioTrack,
      videoTrack
    })
  })

  if (myUserId.value && !list.some(t => t.isLocal)) {
    list.unshift({
      key: `u_${myUserId.value}`,
      userId: myUserId.value,
      name: '你',
      isLocal: true,
      isHost: sameUser(myUserId.value, hostId.value),
      audioEnabled: audioEnabled.value,
      videoEnabled: videoEnabled.value,
      audioTrack: meetingStore.getLocalTrack('audio'),
      videoTrack: meetingStore.getLocalTrack('video')
    })
  }
  return list
})

const selfTile = computed(() => tiles.value.find(t => t.isLocal) || null)
const primaryTile = computed(() => tiles.value.find(t => !t.isLocal && t.videoTrack) || tiles.value.find(t => !t.isLocal) || selfTile.value)

const isReady = computed(() => meetingStore.localState === MEETING_LOCAL_STATE_CONNECTED && !joinError.value && !ended.value)

const stateClass = computed(() => {
  switch (meetingStore.localState) {
    case MEETING_LOCAL_STATE_CONNECTED: return 'connected'
    case MEETING_LOCAL_STATE_RECONNECTING: return 'reconnecting'
    case MEETING_LOCAL_STATE_CONNECTING:
    case MEETING_LOCAL_STATE_JOINING: return 'connecting'
    case MEETING_LOCAL_STATE_LEAVING: return 'leaving'
    case MEETING_LOCAL_STATE_ENDED: return 'ended'
    default: return 'idle'
  }
})

const stateText = computed(() => {
  if (joinError.value) return joinError.value
  if (joining.value) return '正在接入视频画面…'
  switch (meetingStore.localState) {
    case MEETING_LOCAL_STATE_CONNECTED: return '已接通'
    case MEETING_LOCAL_STATE_RECONNECTING: return '重连中'
    case MEETING_LOCAL_STATE_CONNECTING:
    case MEETING_LOCAL_STATE_JOINING: return '连接中'
    case MEETING_LOCAL_STATE_LEAVING: return '正在挂断'
    case MEETING_LOCAL_STATE_ENDED: return '已结束'
    default: return '等待接通'
  }
})

const durationText = computed(() => {
  if (!startedAt.value) return '00:00'
  const diff = Math.max(0, Math.floor((nowTs.value - startedAt.value) / 1000))
  const h = Math.floor(diff / 3600)
  const m = Math.floor((diff % 3600) / 60)
  const s = diff % 60
  const pad = n => (n < 10 ? `0${n}` : String(n))
  return h > 0 ? `${pad(h)}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`
})

const startTimer = () => {
  if (!startedAt.value) {
    const startIso = meetingStore.currentRoom?.started_at || meetingStore.currentRoom?.created_at
    startedAt.value = startIso ? Date.parse(startIso) : Date.now()
  }
  if (!timerHandle) {
    timerHandle = setInterval(() => { nowTs.value = Date.now() }, 1000)
  }
}

const joinMeeting = async () => {
  if (joining.value) return
  const code = formattedCode.value
  if (!digitsOf(code)) {
    joinError.value = '会议号为空'
    return
  }
  joining.value = true
  joinError.value = ''
  ended.value = false
  try {
    const currentCode = formatCode(meetingStore.currentRoom?.room_code || '')
    if (meetingStore.isInMeeting && currentCode && digitsOf(currentCode) === digitsOf(code)) {
      startTimer()
      return
    }
    if (meetingStore.isInMeeting) {
      await meetingStore.leave().catch(() => {})
    }
    meetingStore.devicePreview = {
      audioDeviceId: '',
      videoDeviceId: '',
      speakerDeviceId: '',
      displayName: userStore.userInfo?.nickname || userStore.userInfo?.username || '',
      startAudio: true,
      startVideo: true
    }
    await meetingStore.joinAndEnter(code, '', {
      startAudio: true,
      startVideo: true,
      audioDeviceId: '',
      videoDeviceId: ''
    })
    startTimer()
  } catch (err) {
    joinError.value = err?.message || '视频咨询接入失败'
    console.error('[CloudLawVideoCall] join failed', err)
  } finally {
    joining.value = false
  }
}

const onMicToggle = async () => {
  if (audioLoading.value || leaving.value) return
  audioLoading.value = true
  try {
    if (audioEnabled.value) {
      await meetingStore.stopLocalAudio()
    } else {
      await meetingStore.startLocalAudio()
    }
  } catch (err) {
    uni.showToast({ title: err?.message || '麦克风操作失败', icon: 'none' })
  } finally {
    audioLoading.value = false
  }
}

const onCamToggle = async () => {
  if (videoLoading.value || leaving.value) return
  videoLoading.value = true
  try {
    if (videoEnabled.value) {
      await meetingStore.stopLocalVideo()
    } else {
      await meetingStore.startLocalVideo()
    }
  } catch (err) {
    uni.showToast({ title: err?.message || '摄像头操作失败', icon: 'none' })
  } finally {
    videoLoading.value = false
  }
}

const hangup = async () => {
  if (leaving.value) return
  leaving.value = true
  try {
    await meetingStore.leave()
  } catch (err) {
    console.warn('[CloudLawVideoCall] leave failed', err)
  } finally {
    ended.value = true
    leaving.value = false
  }
}

onLoad(query => {
  rawCode.value = query?.code || query?.roomCode || ''
  joinMeeting()
})

onMounted(() => {
  stateStopWatch = watch(() => meetingStore.localState, s => {
    if (s === MEETING_LOCAL_STATE_CONNECTED) {
      startTimer()
    }
    if (s === MEETING_LOCAL_STATE_ENDED) {
      const reason = meetingStore.lastEndedReason
      const label = MEETING_ENDED_REASON_LABEL?.[reason] || '视频咨询已结束'
      uni.showToast({ title: label, icon: 'none' })
      ended.value = true
    }
  }, { immediate: true })
})

onBeforeUnmount(() => {
  if (timerHandle) {
    clearInterval(timerHandle)
    timerHandle = null
  }
  if (stateStopWatch) {
    stateStopWatch()
    stateStopWatch = null
  }
})

onUnload(() => {
  if (meetingStore.isInMeeting && meetingStore.localState !== MEETING_LOCAL_STATE_LEAVING) {
    meetingStore.leave().catch(() => {})
  }
})
</script>

<style scoped>
.cloud-law-call {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: linear-gradient(180deg, #32175f 0%, #071025 38%, #050b1d 100%);
  color: #ffffff;
  z-index: 1000;
}
.entry-state {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 48rpx;
  box-sizing: border-box;
  text-align: center;
}
.entry-avatar {
  width: 136rpx;
  height: 136rpx;
  border-radius: 50%;
  background: linear-gradient(135deg, #43b1ff 0%, #1974e8 100%);
  border: 4rpx solid rgba(255, 255, 255, 0.75);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 56rpx;
  font-weight: 700;
  box-shadow: 0 18rpx 60rpx rgba(25, 116, 232, 0.36);
}
.entry-title {
  margin-top: 28rpx;
  font-size: 34rpx;
  font-weight: 700;
}
.entry-desc {
  margin-top: 14rpx;
  max-width: 560rpx;
  font-size: 24rpx;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.72);
}
.retry-btn {
  margin-top: 36rpx;
  width: 260rpx;
  height: 76rpx;
  line-height: 76rpx;
  border-radius: 999rpx;
  background: #1664ff;
  color: #ffffff;
  font-size: 28rpx;
}
.call-shell {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}
.remote-stage {
  position: absolute;
  inset: 0;
  background: #050b1d;
}
.main-video {
  width: 100%;
  height: 100%;
  border-radius: 0;
}
.empty-video {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.self-preview {
  position: absolute;
  right: 24rpx;
  bottom: 218rpx;
  width: 210rpx;
  height: 280rpx;
  border-radius: 20rpx;
  overflow: hidden;
  border: 2rpx solid rgba(108, 151, 255, 0.72);
  box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.38);
  background: #0b1220;
  z-index: 5;
}
.self-video {
  width: 100%;
  height: 100%;
  border-radius: 0;
}
.top-info {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  padding: calc(26rpx + env(safe-area-inset-top)) 28rpx 40rpx;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20rpx;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 100%);
  box-sizing: border-box;
  z-index: 6;
}
.call-title {
  display: block;
  font-size: 32rpx;
  font-weight: 700;
  line-height: 1.2;
}
.call-code {
  display: block;
  margin-top: 8rpx;
  font-size: 22rpx;
  color: rgba(255, 255, 255, 0.72);
  letter-spacing: 1rpx;
}
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 8rpx;
  padding: 8rpx 16rpx;
  border-radius: 999rpx;
  background: rgba(21, 128, 61, 0.72);
  color: #baf7d0;
  font-size: 22rpx;
  white-space: nowrap;
}
.status-pill.connecting,
.status-pill.reconnecting {
  background: rgba(37, 99, 235, 0.72);
  color: #dbeafe;
}
.status-pill.ended,
.status-pill.leaving {
  background: rgba(107, 114, 128, 0.72);
  color: #f3f4f6;
}
.status-dot {
  width: 12rpx;
  height: 12rpx;
  border-radius: 50%;
  background: currentColor;
  box-shadow: 0 0 12rpx currentColor;
}
.timer {
  position: absolute;
  left: 50%;
  bottom: 196rpx;
  transform: translateX(-50%);
  padding: 8rpx 20rpx;
  border-radius: 999rpx;
  background: rgba(255, 255, 255, 0.18);
  color: rgba(255, 255, 255, 0.86);
  font-size: 24rpx;
  line-height: 1;
  z-index: 7;
}
.controls {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 34rpx 44rpx calc(34rpx + env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: linear-gradient(0deg, rgba(4, 10, 27, 0.96) 0%, rgba(4, 10, 27, 0.72) 72%, rgba(4, 10, 27, 0) 100%);
  box-sizing: border-box;
  z-index: 8;
}
.control-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14rpx;
  min-width: 150rpx;
}
.control-btn,
.hangup-btn {
  width: 108rpx;
  height: 108rpx;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  box-shadow: inset 0 0 0 2rpx rgba(255, 255, 255, 0.08);
}
.control-btn {
  background: rgba(24, 64, 140, 0.9);
}
.control-btn.disabled {
  background: rgba(18, 32, 70, 0.94);
  color: rgba(255, 255, 255, 0.55);
}
.hangup-btn {
  background: #ff2727;
  box-shadow: 0 10rpx 34rpx rgba(255, 39, 39, 0.38);
}
.control-icon,
.hangup-icon {
  font-size: 34rpx;
  font-weight: 700;
}
.control-label {
  font-size: 24rpx;
  color: rgba(255, 255, 255, 0.74);
}
.loading {
  opacity: 0.68;
}
</style>
