<template>
  <BaseCard title="市场实时监控">
    <div class="monitor-content">
      <div v-if="loading" class="state-box">加载中...</div>
      <div v-else-if="!cameras.length" class="state-box">暂无监控设备</div>
      <template v-else>
        <div class="toolbar">
          <div class="toolbar-left">
            <span class="toolbar-title">监控画面({{ currentGroup + 1 }}/{{ totalGroups }})</span>
            <div class="page-dots">
              <button
                v-for="(_, index) in cameraGroups"
                :key="index"
                type="button"
                class="page-dot"
                :class="{ active: index === currentGroup }"
                :aria-label="`第${index + 1}组监控画面`"
                @click="switchToGroup(index)"
              />
            </div>
          </div>
          <div class="toolbar-actions">
            <button type="button" class="nav-btn nav-btn-arrow" title="上一组" @click="prevGroup">
              <svg viewBox="0 0 24 24" aria-hidden="true">
                <path d="M14 6L8 12L14 18" />
              </svg>
            </button>
            <button
              type="button"
              class="nav-btn nav-btn-play"
              :class="{ active: isAutoPlay }"
              :title="isAutoPlay ? '暂停轮播' : '开始轮播'"
              @click="toggleAutoPlay"
            >
              <svg v-if="isAutoPlay" viewBox="0 0 24 24" aria-hidden="true">
                <path d="M8 6H10V18H8V6ZM14 6H16V18H14V6Z" />
              </svg>
              <svg v-else viewBox="0 0 24 24" aria-hidden="true">
                <path d="M8 5V19L19 12L8 5Z" />
              </svg>
            </button>
            <button type="button" class="nav-btn nav-btn-arrow" title="下一组" @click="nextGroup">
              <svg viewBox="0 0 24 24" aria-hidden="true">
                <path d="M10 6L16 12L10 18" />
              </svg>
            </button>
          </div>
        </div>

        <div class="monitor-grid">
          <article
            v-for="camera in currentCameras"
            :key="camera.id"
            class="monitor-card"
            :class="camera.status"
          >
            <span class="corner corner-tl" aria-hidden="true" />
            <span class="corner corner-tr" aria-hidden="true" />
            <span class="corner corner-bl" aria-hidden="true" />
            <span class="corner corner-br" aria-hidden="true" />

            <div
              :ref="(el) => setScreenRef(camera.id, el)"
              class="card-screen"
            >
              <div class="card-head">
                <span class="card-title">{{ camera.name }}</span>
                <div class="card-actions">
                  <button
                    type="button"
                    class="icon-btn"
                    title="全屏"
                    @click="toggleFullscreen(camera.id)"
                  >
                    <svg viewBox="0 0 24 24" aria-hidden="true">
                      <path d="M5 9H3V3H9V5H5V9ZM15 5V3H21V9H19V5H15ZM19 15H21V21H15V19H19V15ZM9 19V21H3V15H5V19H9Z" />
                    </svg>
                  </button>
                  <button
                    type="button"
                    class="icon-btn"
                    :class="{ recording: camera.recording }"
                    title="录制"
                    @click="toggleRecording(camera.id)"
                  >
                    <svg viewBox="0 0 24 24" aria-hidden="true">
                      <circle cx="12" cy="12" r="8" />
                      <circle class="record-dot" cx="12" cy="12" r="3.5" />
                    </svg>
                  </button>
                </div>
              </div>

              <div class="screen-body">
                <MonitorLivePlayer
                  v-if="camera.isPlaying && camera.canPlay"
                  :player-id="camera.id"
                  :url="camera.ezopenUrl"
                  :access-token="camera.accessToken"
                  :player-config="playerConfig"
                  :active="camera.isPlaying"
                />
                <template v-else>
                  <img
                    v-if="camera.previewUrl"
                    class="screen-media"
                    :src="camera.previewUrl"
                    :alt="camera.name"
                    @error="handlePreviewError(camera.id)"
                  />
                  <div v-else class="screen-fallback" :class="camera.status">
                    <span v-if="camera.status === 'offline'">离线</span>
                    <span v-else-if="camera.status === 'error'">故障</span>
                    <span v-else>{{ camera.name }}</span>
                  </div>
                  <button
                    v-if="camera.canPlay"
                    type="button"
                    class="play-overlay"
                    :title="`播放 ${camera.name}`"
                    @click="startLivePlay(camera.id)"
                  >
                    <svg viewBox="0 0 24 24" aria-hidden="true">
                      <path d="M8 5V19L19 12L8 5Z" />
                    </svg>
                  </button>
                </template>
                <button
                  v-if="camera.isPlaying"
                  type="button"
                  class="stop-live-btn"
                  title="停止播放"
                  @click="stopLivePlay(camera.id)"
                >
                  停止
                </button>
              </div>

              <div class="screen-footer">
                <span class="resolution-text">分辨率:{{ camera.resolution }}</span>
              </div>
            </div>
          </article>
        </div>
      </template>
    </div>
  </BaseCard>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import BaseCard from './BaseCard.vue'
import MonitorLivePlayer from './MonitorLivePlayer.vue'
import { resolveEzopenUrl } from '../utils/ezvizPlayer.js'

const API_URL = '/api/dashboard/market-realtime-monitor'
const PLAYER_CONFIG_URL = '/api/dashboard/market-realtime-player-config'
const DEFAULT_PAGE_SIZE = 2
const DEFAULT_AUTO_PLAY_INTERVAL = 10000

const loading = ref(true)
const cameras = ref([])
const playerConfig = ref({})
const pageSize = ref(DEFAULT_PAGE_SIZE)
const autoPlayInterval = ref(DEFAULT_AUTO_PLAY_INTERVAL)
const currentGroup = ref(0)
const isAutoPlay = ref(true)
const carouselTimer = ref(null)
const screenRefs = new Map()
const brokenPreviews = ref([])
const playingIds = ref(new Set())

const isPreviewBroken = (cameraId) => brokenPreviews.value.includes(cameraId)

const resolvePreviewUrl = (item) => item.url || item.preview || item.previewUrl || item.preview_img_url || ''

const normalizeCamera = (item) => ({
  id: item.id,
  name: item.name || '未命名监控',
  status: item.status || 'online',
  resolution: (item.resolution || '1920x1080').replace('×', 'x'),
  recording: false,
  preview: resolvePreviewUrl(item),
  hdPlayUrl: item.hdPlayUrl || item.hd_play_url || '',
  playUrl: item.playUrl || item.play_url || '',
  ezopenUrl: item.ezopenUrl || item.ezopen_url || '',
  accessToken: item.accessToken || item.access_token || '',
  serialNumber: item.serialNumber || item.serial_number || '',
  channelNumber: item.channelNumber ?? item.channel_number ?? 1
})

const cameraGroups = computed(() => {
  const groups = []
  const size = Math.max(1, pageSize.value)
  for (let i = 0; i < cameras.value.length; i += size) {
    groups.push(cameras.value.slice(i, i + size))
  }
  return groups.length ? groups : [[]]
})

const totalGroups = computed(() => cameraGroups.value.length)

const currentCameras = computed(() => {
  const group = cameraGroups.value[currentGroup.value] || []
  return group.map((camera) => {
    const ezopenUrl = resolveEzopenUrl(camera, playerConfig.value)
    const isPlaying = playingIds.value.has(camera.id)
    return {
      ...camera,
      previewUrl: isPreviewBroken(camera.id) ? '' : camera.preview,
      ezopenUrl,
      canPlay: Boolean(ezopenUrl && camera.accessToken && camera.status === 'online'),
      isPlaying
    }
  })
})

const clearLivePlay = () => {
  playingIds.value = new Set()
}

const startLivePlay = (cameraId) => {
  const next = new Set(playingIds.value)
  next.add(cameraId)
  playingIds.value = next
  isAutoPlay.value = false
  stopAutoPlay()
}

const stopLivePlay = (cameraId) => {
  const next = new Set(playingIds.value)
  next.delete(cameraId)
  playingIds.value = next
}

const setScreenRef = (cameraId, el) => {
  if (el) {
    screenRefs.set(cameraId, el)
  } else {
    screenRefs.delete(cameraId)
  }
}

const handlePreviewError = (cameraId) => {
  if (!brokenPreviews.value.includes(cameraId)) {
    brokenPreviews.value = [...brokenPreviews.value, cameraId]
  }
}

const toggleFullscreen = async (cameraId) => {
  const element = screenRefs.get(cameraId)
  if (!element) {
    return
  }
  try {
    if (document.fullscreenElement) {
      await document.exitFullscreen()
      return
    }
    await element.requestFullscreen()
  } catch (error) {
    console.error('全屏切换失败:', error)
  }
}

const toggleRecording = (cameraId) => {
  const camera = cameras.value.find((item) => item.id === cameraId)
  if (camera && camera.status === 'online') {
    camera.recording = !camera.recording
  }
}

const nextGroup = () => {
  if (!totalGroups.value) {
    return
  }
  clearLivePlay()
  currentGroup.value = (currentGroup.value + 1) % totalGroups.value
}

const prevGroup = () => {
  if (!totalGroups.value) {
    return
  }
  clearLivePlay()
  currentGroup.value = currentGroup.value === 0
    ? totalGroups.value - 1
    : currentGroup.value - 1
}

const switchToGroup = (index) => {
  if (index === currentGroup.value) {
    return
  }
  clearLivePlay()
  currentGroup.value = index
  resetAutoPlayTimer()
}

const stopAutoPlay = () => {
  if (carouselTimer.value) {
    clearInterval(carouselTimer.value)
    carouselTimer.value = null
  }
}

const startAutoPlay = () => {
  stopAutoPlay()
  if (totalGroups.value <= 1) {
    return
  }
  carouselTimer.value = setInterval(() => {
    nextGroup()
  }, autoPlayInterval.value)
}

const resetAutoPlayTimer = () => {
  if (isAutoPlay.value) {
    startAutoPlay()
  }
}

const toggleAutoPlay = () => {
  isAutoPlay.value = !isAutoPlay.value
  if (isAutoPlay.value) {
    startAutoPlay()
  } else {
    stopAutoPlay()
  }
}

const loadPlayerConfig = async () => {
  try {
    const response = await fetch(PLAYER_CONFIG_URL)
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }
    const result = await response.json()
    if (result.code !== undefined && result.code !== 1) {
      throw new Error(result.message || '播放配置返回异常')
    }
    playerConfig.value = result.data ?? result
  } catch (error) {
    console.error('加载监控播放配置失败，使用内置规则:', error)
    playerConfig.value = {}
  }
}

const loadData = async () => {
  loading.value = true
  try {
    await loadPlayerConfig()
    const response = await fetch(API_URL)
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }
    const result = await response.json()
    if (result.code !== undefined && result.code !== 1) {
      throw new Error(result.message || '接口返回异常')
    }
    const payload = result.data ?? result
    const list = Array.isArray(payload.cameras) ? payload.cameras : []
    cameras.value = list.map(normalizeCamera)
    pageSize.value = Number(payload.pageSize) || DEFAULT_PAGE_SIZE
    autoPlayInterval.value = Number(payload.autoPlayInterval) || DEFAULT_AUTO_PLAY_INTERVAL
    brokenPreviews.value = []
    clearLivePlay()
  } catch (error) {
    console.error('加载市场实时监控失败:', error)
    cameras.value = []
  } finally {
    loading.value = false
    if (currentGroup.value >= totalGroups.value) {
      currentGroup.value = 0
    }
    if (isAutoPlay.value) {
      startAutoPlay()
    }
  }
}

onMounted(async () => {
  await loadData()
  window.addEventListener('dataReloaded', loadData)
})

onUnmounted(() => {
  stopAutoPlay()
  window.removeEventListener('dataReloaded', loadData)
})
</script>

<style scoped>
.monitor-content {
  width: 100%;
  height: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-sizing: border-box;
}

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

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-shrink: 0;
  min-height: 24px;
}

.toolbar-left {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.toolbar-title {
  font-family: 'Microsoft YaHei', sans-serif;
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.88);
  white-space: nowrap;
}

.page-dots {
  display: flex;
  align-items: center;
  gap: 6px;
}

.page-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.28);
  cursor: pointer;
  transition: background 0.2s ease, transform 0.2s ease;
}

.page-dot.active {
  background: #06c7be;
  box-shadow: 0 0 6px rgba(6, 199, 190, 0.55);
}

.page-dot:hover {
  transform: scale(1.15);
}

.toolbar-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.nav-btn {
  width: 24px;
  height: 24px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(6, 199, 190, 0.75);
  border-radius: 2px;
  background: rgba(2, 47, 54, 0.72);
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.nav-btn:hover,
.nav-btn.active {
  background: rgba(6, 199, 190, 0.18);
  border-color: #06c7be;
}

.nav-btn svg {
  width: 14px;
  height: 14px;
}

.nav-btn-arrow svg {
  fill: none;
  stroke: #06c7be;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.nav-btn-play svg {
  fill: #06c7be;
}

.monitor-grid {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.monitor-card {
  position: relative;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: rgba(4, 28, 38, 0.72);
  border: 1px solid rgba(6, 199, 190, 0.35);
  box-sizing: border-box;
  overflow: hidden;
}

.card-screen {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  background: #0a1418;
  overflow: hidden;
}

.card-head {
  flex: 0 0 30px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  height: 30px;
  padding: 0 8px;
  background: rgba(16, 150, 161, 0.3);
  box-sizing: border-box;
}

.card-title {
  font-family: 'Microsoft YaHei', sans-serif;
  font-size: 14px;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.92);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.corner {
  position: absolute;
  width: 10px;
  height: 10px;
  border: 2px solid #06c7be;
  pointer-events: none;
  z-index: 3;
}

.corner-tl {
  top: 0;
  left: 0;
  border-right: none;
  border-bottom: none;
}

.corner-tr {
  top: 0;
  right: 0;
  border-left: none;
  border-bottom: none;
}

.corner-bl {
  bottom: 0;
  left: 0;
  border-right: none;
  border-top: none;
}

.corner-br {
  bottom: 0;
  right: 0;
  border-left: none;
  border-top: none;
}

.card-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.icon-btn {
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.icon-btn svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: #06c7be;
  stroke-width: 1.6;
}

.icon-btn svg path {
  fill: none;
  stroke: #06c7be;
}

.icon-btn svg .record-dot {
  fill: transparent;
  stroke: none;
}

.icon-btn.recording svg .record-dot {
  fill: #ff4d4f;
}

.screen-body {
  flex: 1 1 auto;
  min-height: 0;
  position: relative;
  overflow: hidden;
}

.play-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  border: none;
  background: rgba(4, 20, 28, 0.35);
  cursor: pointer;
  transition: background 0.2s ease;
}

.play-overlay:hover {
  background: rgba(4, 20, 28, 0.5);
}

.play-overlay svg {
  width: 48px;
  height: 48px;
  fill: rgba(255, 255, 255, 0.92);
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.45));
  transition: transform 0.2s ease;
}

.play-overlay:hover svg {
  transform: scale(1.08);
}

.stop-live-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 3;
  padding: 2px 10px;
  border: 1px solid rgba(6, 199, 190, 0.75);
  border-radius: 2px;
  background: rgba(2, 47, 54, 0.82);
  color: rgba(255, 255, 255, 0.9);
  font-family: 'Microsoft YaHei', sans-serif;
  font-size: 12px;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.stop-live-btn:hover {
  background: rgba(6, 199, 190, 0.22);
  border-color: #06c7be;
}

.screen-body :deep(.ezviz-player-host),
.screen-body :deep(.screen-media),
.screen-media,
.screen-fallback {
  display: block;
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
}

.screen-media {
  object-fit: cover;
  display: block;
}

.screen-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Microsoft YaHei', sans-serif;
  font-size: 14px;
  color: rgba(255, 255, 255, 0.72);
  background: linear-gradient(180deg, rgba(8, 36, 44, 0.95) 0%, rgba(4, 20, 28, 0.98) 100%);
}

.screen-fallback.offline {
  color: #bf7412;
}

.screen-fallback.error {
  color: #fe4a46;
}

.screen-footer {
  flex: 0 0 24px;
  display: flex;
  align-items: center;
  height: 24px;
  padding: 0 8px;
  background: rgba(16, 150, 161, 0.3);
  box-sizing: border-box;
  pointer-events: none;
}

.resolution-text {
  font-family: 'Microsoft YaHei', sans-serif;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.82);
  line-height: 1.2;
}

.monitor-card.offline {
  border-color: rgba(191, 116, 18, 0.45);
}

.monitor-card.error {
  border-color: rgba(254, 74, 70, 0.45);
}
</style>
