<template>
  <div class="warehouse-page">
    <section class="warehouse-hero">
      <button type="button" class="back-btn" @click="goBack">
        <span></span>
        返回首页
      </button>
      <div>
        <p>{{ projectName || '项目仓库' }}</p>
        <h1>{{ warehouseName || '仓库传感器看板' }}</h1>
        <em>实时汇聚仓库内全部传感器状态、指标与最新上报数据</em>
      </div>
      <div class="hero-meta">
        <strong>{{ onlineCount }}/{{ sensorCards.length }}</strong>
        <span>在线设备</span>
      </div>
    </section>

    <section class="summary-grid">
      <div class="summary-card">
        <span>设备总数</span>
        <strong>{{ sensorCards.length }}</strong>
      </div>
      <div class="summary-card online">
        <span>在线设备</span>
        <strong>{{ onlineCount }}</strong>
      </div>
      <div class="summary-card offline">
        <span>离线设备</span>
        <strong>{{ offlineCount }}</strong>
      </div>
      <div class="summary-card">
        <span>最近上报</span>
        <strong>{{ latestReportTime || '--' }}</strong>
      </div>
    </section>

    <section v-loading="loading" class="sensor-grid">
      <div v-if="!loading && sensorCards.length === 0 && !hasCameraCard" class="empty-state">
        当前仓库暂无传感器设备
      </div>

      <article v-for="item in sensorCards" :key="item.cardKey" :class="['sensor-card', item.statusClass]">
        <div class="sensor-content">
          <div class="sensor-main">
            <div class="sensor-top">
              <div :class="['sensor-icon', item.type]">
                <span>{{ item.icon }}</span>
              </div>
              <div class="sensor-state">
                <i></i>
                {{ item.onlineText }}
              </div>
            </div>

            <div class="sensor-body">
              <p>{{ item.locationName }}</p>
              <h2>{{ item.typeName }}</h2>
              <strong>{{ item.summary }}</strong>
              <div v-if="isSocketControlType(item.type)" class="socket-actions">
                <button
                  type="button"
                  class="control-btn control-on"
                  :disabled="!!controlLoadingMap[item.cardKey]"
                  @click="handleSocketControl(item, 'on')"
                >
                  {{ getSocketActionText(item.type, 'on', !!controlLoadingMap[item.cardKey]) }}
                </button>
                <button
                  type="button"
                  class="control-btn control-off"
                  :disabled="!!controlLoadingMap[item.cardKey]"
                  @click="handleSocketControl(item, 'off')"
                >
                  {{ getSocketActionText(item.type, 'off', !!controlLoadingMap[item.cardKey]) }}
                </button>
              </div>
              <div v-else-if="item.type === 'switch'" class="switch-actions">
                <div
                  v-for="channel in [1, 2, 3]"
                  :key="`${item.cardKey}-channel-${channel}`"
                  class="switch-action-row"
                >
                  <span class="switch-channel-label">开关{{ channel }}</span>
                  <button
                    type="button"
                    class="control-btn control-on"
                    :disabled="!!controlLoadingMap[`${item.cardKey}-channel-${channel}`]"
                    @click="handleLightSwitchControl(item, channel, 'on')"
                  >
                    {{ getLightSwitchActionText(channel, 'on', !!controlLoadingMap[`${item.cardKey}-channel-${channel}`]) }}
                  </button>
                  <button
                    type="button"
                    class="control-btn control-off"
                    :disabled="!!controlLoadingMap[`${item.cardKey}-channel-${channel}`]"
                    @click="handleLightSwitchControl(item, channel, 'off')"
                  >
                    {{ getLightSwitchActionText(channel, 'off', !!controlLoadingMap[`${item.cardKey}-channel-${channel}`]) }}
                  </button>
                </div>
              </div>
            </div>
          </div>

          <div class="metric-panel">
            <p>实时指标</p>
            <div class="metric-list">
              <span v-for="metric in item.metrics" :key="metric.label">
                <em>{{ metric.label }}</em>
                <strong>{{ metric.value }}</strong>
              </span>
              <span v-if="item.metrics.length === 0" class="muted">
                <em>状态</em>
                <strong>等待数据</strong>
              </span>
            </div>
          </div>
        </div>

        <div class="sensor-footer">
          <span>DevEUI：{{ item.devEui || '--' }}</span>
          <span>{{ item.reportTime || '暂无上报' }}</span>
        </div>
      </article>

      <article v-if="hasCameraCard" :class="['sensor-card', 'camera-card', cameraReady ? 'camera-active' : '']">
        <div class="sensor-content">
          <div class="sensor-main">
            <div class="sensor-top">
              <div class="sensor-icon camera">
                <span>监</span>
              </div>
              <div class="sensor-state">
                <i></i>
                {{ cameraReady ? '播放中' : (cameraLoading ? '加载中' : '已绑定') }}
              </div>
            </div>

            <div class="sensor-body">
              <p>{{ warehouseName || '仓库摄像头' }}</p>
              <h2>仓库摄像头</h2>
              <strong>{{ cameraSummaryText }}</strong>
              <div class="socket-actions">
                <button
                    type="button"
                    class="control-btn control-on"
                    :disabled="cameraLoading"
                    @click="handleCameraPlay"
                >
                  {{ cameraLoading ? '加载中...' : (cameraPlaying ? '重新播放' : '开始播放') }}
                </button>
              </div>
            </div>
          </div>

          <div class="metric-panel camera-panel">
            <p>实时画面</p>
            <CameraPlayer
              ref="cameraPlayerRef"
              :device-serial="cameraDeviceSerial"
              @state-change="handleCameraStateChange"
            />
            <div v-if="cameraError" class="camera-error">{{ cameraError }}</div>
            <div v-else-if="cameraExpireTime" class="camera-expire">地址有效期至 {{ cameraExpireTime }}</div>
          </div>
        </div>

        <div class="sensor-footer">
          <span>DeviceSerial：{{ cameraDeviceSerial }}</span>
          <span>{{ cameraReady ? '视频已开始播放' : (cameraLoading ? '正在建立视频连接' : cameraStatusText) }}</span>
        </div>
      </article>
    </section>
  </div>
</template>

<script setup name="WarehouseDashboard">
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { listMqttDevice } from '@/api/worn/mqttDevice'
import { listMqttData } from '@/api/worn/mqttData'
import { listMqttEvent } from '@/api/worn/mqttEvent'
import { controlLightSwitch, controlSocket } from '@/api/worn/socket'
import { closeWs, connectWs } from '@/utils/ws'
import CameraPlayer from './components/CameraPlayer.vue'

const route = useRoute()
const router = useRouter()

const loading = ref(false)
const devices = ref([])
const latestDataMap = reactive({})
const controlLoadingMap = reactive({})
const ENV_CARD_TYPES = ['tempHum', 'toxicGas', 'hydrogenSulfide']
const cameraPlayerRef = ref(null)
const cameraLoading = ref(false)
const cameraPlaying = ref(false)
const cameraExpireTime = ref('')
const cameraError = ref('')
const cameraReady = ref(false)
const cameraStatusText = ref('点击开始播放摄像头')

const deptId = computed(() => route.query.deptId)
const projectName = computed(() => route.query.projectName || '')
const warehouseName = computed(() => route.query.warehouseName || '')
const cameraDeviceSerial = computed(() => String(route.query.deviceSerial || '').trim())
const hasCameraCard = computed(() => !!cameraDeviceSerial.value)
const cameraSummaryText = computed(() => {
  if (cameraError.value) {
    return '播放失败，请重新尝试'
  }
  if (cameraLoading.value) {
    return '正在获取摄像头视频流'
  }
  if (cameraReady.value || cameraPlaying.value) {
    return '摄像头画面播放中'
  }
  return '点击卡片开始播放摄像头画面'
})

const sensorCards = computed(() => devices.value.flatMap((device) => expandDeviceCards(device)))
const onlineCount = computed(() => sensorCards.value.filter((item) => item.isOnline).length)
const offlineCount = computed(() => Math.max(sensorCards.value.length - onlineCount.value, 0))
const latestReportTime = computed(() => {
  const times = sensorCards.value.map((item) => item.reportTime).filter(Boolean)
  return times[0] || ''
})
function goBack() {
  router.push('/index')
}

async function handleCameraPlay() {
  if (!cameraDeviceSerial.value || cameraLoading.value) {
    return
  }

  cameraPlayerRef.value?.play?.().catch((error) => {
    const message = error?.message || '摄像头播放失败'
    cameraError.value = message
    cameraStatusText.value = message
    ElMessage.error(message)
  })
}

function handleCameraFullscreen() {
  cameraPlayerRef.value?.fullscreen?.()
}

function handleCameraStateChange(state = {}) {
  cameraLoading.value = !!state.loading
  cameraReady.value = !!state.ready
  cameraPlaying.value = !!state.playing
  cameraError.value = state.error || ''
  cameraExpireTime.value = state.expireTime || ''
  cameraStatusText.value = state.statusText || '点击开始播放摄像头'
}

function resetCameraState() {
  cameraLoading.value = false
  cameraReady.value = false
  cameraPlaying.value = false
  cameraStatusText.value = '点击开始播放摄像头'
}

function loadDevices() {
  if (!deptId.value) {
    devices.value = []
    return
  }

  loading.value = true
  listMqttDevice({
    pageNum: 1,
    pageSize: 100,
    deptId: deptId.value
  }).then((res) => {
    devices.value = res.rows || []
    return loadLatestData()
  }).finally(() => {
    loading.value = false
  })
}

function loadLatestData() {
  const tasks = devices.value.map((device) => {
    if (!device.id) {
      return Promise.resolve()
    }

    const requestList = [
      listMqttData({
        pageNum: 1,
        pageSize: 1,
        deviceId: device.id
      }).then((res) => {
        const row = (res.rows || [])[0]
        if (row) {
          latestDataMap[getDeviceKey(device)] = normalizePayload({
            ...(latestDataMap[getDeviceKey(device)] || {}),
            ...row
          })
        }
      }).catch(() => {})
    ]

    if (isStateDevice(device)) {
      requestList.push(
        listMqttEvent({
          pageNum: 1,
          pageSize: 1,
          deviceId: device.id
        }).then((res) => {
          const row = (res.rows || [])[0]
          if (row) {
            latestDataMap[getDeviceKey(device)] = normalizePayload({
              ...(latestDataMap[getDeviceKey(device)] || {}),
              ...row,
              ...deriveStateFromEvent(row)
            })
          }
        }).catch(() => {})
      )
    }

    return Promise.all(requestList)
  })

  return Promise.all(tasks)
}

function handleWsMessage(data) {
  if (!data || String(data.deptId || '') !== String(deptId.value || '')) {
    return
  }

  const runtimeStatus = data.deviceStatus !== undefined && data.deviceStatus !== null && data.deviceStatus !== ''
    ? String(data.deviceStatus)
    : null

  const index = devices.value.findIndex((device) => {
    return String(device.id || '') === String(data.deviceId || '') ||
      String(device.devEui || '').toLowerCase() === String(data.devEUI || data.devEui || '').toLowerCase()
  })

  if (index > -1) {
    const device = devices.value[index]
    const key = getDeviceKey(device)
    const patchedData = patchRealtimeState(latestDataMap[key] || {}, data)
    latestDataMap[key] = normalizePayload({
      ...(latestDataMap[key] || {}),
      ...patchedData
    })

    devices.value[index] = {
      ...device,
      status: runtimeStatus ?? '0',
      deviceName: data.deviceName || device.deviceName
    }
    return
  }

  const key = getDeviceKey(data)
  const patchedData = patchRealtimeState(latestDataMap[key] || {}, data)
  latestDataMap[key] = normalizePayload({
    ...(latestDataMap[key] || {}),
    ...patchedData
  })
}

function patchRealtimeState(previous, incoming) {
  const next = { ...incoming }

  const switch1 = incoming.switch_1 ?? incoming.switch1
  const switch2 = incoming.switch_2 ?? incoming.switch2
  const switch3 = incoming.switch_3 ?? incoming.switch3

  if (switch1 !== undefined && switch1 !== null && switch1 !== '') {
    next.switch_1 = switch1
    next.switch1 = switch1
  }
  if (switch2 !== undefined && switch2 !== null && switch2 !== '') {
    next.switch_2 = switch2
    next.switch2 = switch2
  }
  if (switch3 !== undefined && switch3 !== null && switch3 !== '') {
    next.switch_3 = switch3
    next.switch3 = switch3
  }

  const waterStatus = deriveWaterStatus({ ...previous, ...incoming })
  if (waterStatus !== undefined) {
    next.waterStatus = waterStatus
    next.water_status = waterStatus
  }

  const doorStatus = deriveDoorStatus({ ...previous, ...incoming })
  if (doorStatus !== undefined) {
    next.doorStatus = doorStatus
    next.door_status = doorStatus
  }

  const socketStatus = deriveSocketStatus({ ...previous, ...incoming })
  if (socketStatus !== undefined) {
    next.switchStatus = socketStatus
    next.socket_status = socketStatus
    next.socketStatus = socketStatus
  }

  return next
}

async function handleSocketControl(item, action) {
  const deviceId = item.id || item.deviceId
  const devEui = item.devEui || item.devEUI || item.dev_eui
  if (!devEui) {
    ElMessage.error('未找到设备 DevEUI，无法下发指令')
    return
  }

  const loadingKey = item.cardKey || String(deviceId)
  if (controlLoadingMap[loadingKey]) {
    return
  }

  controlLoadingMap[loadingKey] = true
  try {
    const status = action === 'on' ? 1 : 0
    await controlSocket({ devEui, deviceId, status })
    syncSocketLocalState(item, status)
    ElMessage.success(getSocketSuccessMessage(item.type, action))
  } finally {
    controlLoadingMap[loadingKey] = false
  }
}

async function handleLightSwitchControl(item, channel, action) {
  const devEui = item.devEui || item.devEUI || item.dev_eui
  if (!devEui) {
    ElMessage.error('未找到设备 DevEUI，无法下发指令')
    return
  }

  const loadingKey = `${item.cardKey}-channel-${channel}`
  if (controlLoadingMap[loadingKey]) {
    return
  }

  controlLoadingMap[loadingKey] = true
  try {
    const status = action === 'on' ? 1 : 0
    await controlLightSwitch({ devEui, channel, status })
    syncLightSwitchLocalState(item, channel, status)
    ElMessage.success(`${action === 'on' ? '开启' : '关闭'}${channel}路指令已发送`)
  } finally {
    controlLoadingMap[loadingKey] = false
  }
}

function syncSocketLocalState(item, status) {
  const key = getDeviceKey(item)
  if (!key) {
    return
  }

  const current = latestDataMap[key] || {}
  const now = new Date().toISOString()
  latestDataMap[key] = normalizePayload({
    ...current,
    deviceId: item.deviceId || item.id,
    devEui: item.devEui || item.devEUI || item.dev_eui,
    deviceName: item.deviceName,
    socket_status: status,
    socketStatus: status,
    switchStatus: status,
    status,
    gatewayTime: current.gatewayTime || now,
    createTime: now
  })
}

function syncLightSwitchLocalState(item, channel, status) {
  const key = getDeviceKey(item)
  if (!key) {
    return
  }

  const current = latestDataMap[key] || {}
  const now = new Date().toISOString()
  latestDataMap[key] = normalizePayload({
    ...current,
    deviceId: item.deviceId || item.id,
    devEui: item.devEui || item.devEUI || item.dev_eui,
    deviceName: item.deviceName,
    [`switch_${channel}`]: status,
    [`switch${channel}`]: status,
    gatewayTime: current.gatewayTime || now,
    createTime: now
  })
}

function expandDeviceCards(device) {
  const data = latestDataMap[getDeviceKey(device)] || {}
  const type = getSensorType(device, data)
  if (type === 'env') {
    return ENV_CARD_TYPES.map((displayType) => buildSensorCard(device, displayType))
  }
  return [buildSensorCard(device)]
}

function buildSensorCard(device, displayType) {
  const data = latestDataMap[getDeviceKey(device)] || {}
  const sourceType = getSensorType(device, data)
  const type = displayType || sourceType
  const metrics = buildDisplayMetrics(data, type)
  const reportTime = formatTime(data.createTime || data.gatewayTime || data.time)
  const isOnline = String(device.status) === '0'

  return {
    ...device,
    cardKey: `${getDeviceKey(device) || device.id || device.devEui || 'sensor'}-${type}`,
    sourceType,
    type,
    icon: getDisplayIcon(type),
    typeName: getDisplayTypeName(type),
    locationName: getLocationName(device, data),
    summary: getDisplaySummary(data, type),
    metrics,
    reportTime,
    isOnline,
    onlineText: isOnline ? '在线' : '离线',
    statusClass: isOnline ? 'online' : 'offline'
  }
}

function isSocketControlType(type) {
  return type === 'socket' || type === 'acSocket'
}

function getSocketSuccessMessage(type, action) {
  if (type === 'acSocket') {
    return action === 'on' ? '开启空调指令已发送' : '关闭空调指令已发送'
  }
  return action === 'on' ? '开启排风指令已发送' : '关闭排风指令已发送'
}

function getSocketActionText(type, action, loading) {
  if (loading) {
    return '发送中...'
  }

  if (type === 'acSocket') {
    return action === 'on' ? '开启空调' : '关闭空调'
  }

  return action === 'on' ? '开启排风' : '关闭排风'
}

function getLightSwitchActionText(channel, action, loading) {
  if (loading) {
    return '发送中...'
  }

  return action === 'on' ? `开${channel}路` : `关${channel}路`
}

function getDisplayIcon(type) {
  if (type === 'acSocket') {
    return '空'
  }
  return getSensorIcon(type)
}

function getDisplayTypeName(type) {
  if (type === 'acSocket') {
    return '智能空调插座'
  }
  return getSensorTypeName(type)
}

function getDisplaySummary(data, type) {
  if (type === 'acSocket') {
    return `空调状态：${translateSwitch(data.switchStatus ?? data.socket_status ?? data.socketStatus ?? data.status)}`
  }
  return getSensorSummary(data, type)
}

function buildDisplayMetrics(data, type) {
  const metrics = buildMetrics(data, type === 'acSocket' ? 'socket' : type)
  if (type !== 'acSocket') {
    return metrics
  }

  return metrics.map((metric, index) => {
    if (index === 0 && String(metric.label).includes('插座')) {
      return { ...metric, label: '空调状态' }
    }
    return metric
  })
}

function getDeviceKey(device) {
  return device.deviceId || device.id || device.devEUI || device.devEui || device.dev_eui
}

function normalizePayload(row) {
  const json = parseJson(row.dataJson) || parseJson(row.payload) || {}
  const nestedData = typeof row.data === 'object'
    ? row.data
    : (typeof row.data === 'string' ? parseJson(row.data) : null) || {}
  const inferredWaterStatus = deriveWaterStatus({
    ...json,
    ...nestedData,
    ...row
  })

  return {
    ...json,
    ...nestedData,
    ...row,
    devEUI: row.devEUI || row.devEui || json.devEUI || json.devEui,
    deviceName: row.deviceName || json.deviceName || nestedData.deviceName,
    waterStatus: row.waterStatus ?? row.water_status ?? nestedData.waterStatus ?? nestedData.water_status ?? json.waterStatus ?? json.water_status ?? inferredWaterStatus,
    water_status: row.water_status ?? row.waterStatus ?? nestedData.water_status ?? nestedData.waterStatus ?? json.water_status ?? json.waterStatus ?? inferredWaterStatus,
    socket_status: row.socket_status ?? row.socketStatus ?? nestedData.socket_status ?? nestedData.socketStatus ?? json.socket_status ?? json.socketStatus,
    switchStatus: row.switchStatus ?? row.socket_status ?? row.socketStatus ?? nestedData.switchStatus ?? nestedData.socket_status ?? nestedData.socketStatus ?? json.switchStatus ?? json.socket_status ?? json.socketStatus,
    switch_1: row.switch_1 ?? row.switch1 ?? nestedData.switch_1 ?? nestedData.switch1 ?? json.switch_1 ?? json.switch1,
    switch1: row.switch1 ?? row.switch_1 ?? nestedData.switch1 ?? nestedData.switch_1 ?? json.switch1 ?? json.switch_1,
    switch_2: row.switch_2 ?? row.switch2 ?? nestedData.switch_2 ?? nestedData.switch2 ?? json.switch_2 ?? json.switch2,
    switch2: row.switch2 ?? row.switch_2 ?? nestedData.switch2 ?? nestedData.switch_2 ?? json.switch2 ?? json.switch_2,
    switch_3: row.switch_3 ?? row.switch3 ?? nestedData.switch_3 ?? nestedData.switch3 ?? json.switch_3 ?? json.switch3,
    switch3: row.switch3 ?? row.switch_3 ?? nestedData.switch3 ?? nestedData.switch_3 ?? json.switch3 ?? json.switch_3,
    doorStatus: row.doorStatus ?? row.door_status ?? nestedData.doorStatus ?? nestedData.door_status ?? json.doorStatus ?? json.door_status,
    tamperStatus: row.tamperStatus ?? row.tamper_status ?? nestedData.tamperStatus ?? nestedData.tamper_status ?? json.tamperStatus ?? json.tamper_status
  }
}

function parseJson(value) {
  if (!value || typeof value !== 'string') {
    return null
  }

  try {
    return JSON.parse(value)
  } catch (e) {
    return null
  }
}

function deriveStateFromEvent(row) {
  const eventType = String(row.eventType || row.event_type || row.event || '').trim().toLowerCase()
  const eventDesc = String(row.eventDesc || row.event_desc || row.statusDesc || row.status_desc || '').trim().toLowerCase()
  const normalized = {
    eventType: row.eventType || row.event_type,
    eventDesc: row.eventDesc || row.event_desc,
    statusDesc: row.statusDesc || row.status_desc
  }

  if (eventType === 'door_open' || eventDesc.includes('门已打开')) {
    normalized.doorStatus = 1
  } else if (eventType === 'door_close' || eventDesc.includes('门已关闭')) {
    normalized.doorStatus = 0
  }

  if (eventType === 'socket_on' || eventDesc.includes('插座通电')) {
    normalized.socket_status = 1
    normalized.switchStatus = 1
  } else if (eventType === 'socket_off' || eventDesc.includes('插座关闭') || eventDesc.includes('插座断电')) {
    normalized.socket_status = 0
    normalized.switchStatus = 0
  }

  const inferredWaterStatus = deriveWaterStatus(row)
  if (inferredWaterStatus !== undefined) {
    normalized.waterStatus = inferredWaterStatus
    normalized.water_status = inferredWaterStatus
  }

  return normalized
}

function deriveWaterStatus(data) {
  const eventType = String(data.eventType || data.event_type || data.event || data.status || '').trim().toLowerCase()
  const eventDesc = String(data.eventDesc || data.event_desc || data.statusDesc || data.status_desc || '').trim().toLowerCase()

  if (eventType === 'alarm' || eventDesc.includes('浸水预警') || eventDesc.includes('水浸预警') || eventDesc.includes('浸水报警') || eventDesc.includes('水浸报警')) {
    return 1
  }

  if (eventType === 'recovery' || eventType === 'normal' || eventDesc.includes('水浸正常') || eventDesc.includes('浸水正常')) {
    return 0
  }

  const directValue = data.waterStatus ?? data.water_status ?? data.water
  if (directValue !== undefined && directValue !== null && directValue !== '') {
    return directValue
  }

  return undefined
}

function deriveDoorStatus(data) {
  const directValue = data.doorStatus ?? data.door_status
  if (directValue !== undefined && directValue !== null && directValue !== '') {
    return directValue
  }

  const eventType = String(data.eventType || data.event_type || data.event || data.status || '').trim().toLowerCase()
  const eventDesc = String(data.eventDesc || data.event_desc || data.statusDesc || data.status_desc || '').trim().toLowerCase()

  if (eventType === 'door_open' || eventDesc.includes('门已打开')) {
    return 1
  }

  if (eventType === 'door_close' || eventDesc.includes('门已关闭')) {
    return 0
  }

  return undefined
}

function deriveSocketStatus(data) {
  const directValue = data.switchStatus ?? data.socket_status ?? data.socketStatus
  if (directValue !== undefined && directValue !== null && directValue !== '') {
    return directValue
  }

  const eventType = String(data.eventType || data.event_type || data.event || data.status || '').trim().toLowerCase()
  const eventDesc = String(data.eventDesc || data.event_desc || data.statusDesc || data.status_desc || '').trim().toLowerCase()

  if (eventType === 'socket_on' || eventDesc.includes('插座通电')) {
    return 1
  }

  if (eventType === 'socket_off' || eventDesc.includes('插座关闭') || eventDesc.includes('插座断电')) {
    return 0
  }

  return undefined
}

function getSensorType(device, data) {
  const deviceType = String(device.deviceType || '').trim().toLowerCase()
  const deviceName = String(device.deviceName || '').trim().toLowerCase()
  const raw = `${deviceType} ${deviceName} ${data.type || ''}`.toLowerCase()

  if (deviceType === 'smoke') return 'smoke'
  if (deviceType === 'water') return 'water'
  if (deviceType === 'door') return 'door'
  if (deviceType === 'env') return 'env'
  if (deviceType === 'switch') return 'switch'
  if (deviceType === 'socket') {
    if (deviceName.includes('ac-socket') || deviceName.includes('ac socket') || deviceName.includes('空调')) {
      return 'acSocket'
    }
    return 'socket'
  }

  if (raw.includes('smoke')) return 'smoke'
  if (raw.includes('water')) return 'water'
  if (raw.includes('ac-socket') || raw.includes('ac socket') || raw.includes('空调')) return 'acSocket'
  if (raw.includes('socket')) return 'socket'
  if (raw.includes('switch')) return 'switch'
  if (raw.includes('door')) return 'door'
  if (raw.includes('env') || raw.includes('temperature') || raw.includes('humidity')) return 'env'
  return 'sensor'
}

function isStateDevice(device) {
  const raw = `${device.deviceType || ''} ${device.deviceName || ''}`.toLowerCase()
  return raw.includes('door') || raw.includes('socket') || raw.includes('switch')
}

function getSensorIcon(type) {
  const iconMap = {
    smoke: '烟',
    water: '水',
    socket: '电',
    switch: '灯',
    door: '门',
    env: '温',
    tempHum: '温',
    toxicGas: '氨',
    hydrogenSulfide: '硫',
    sensor: '测'
  }
  return iconMap[type] || '测'
}

function getSensorTypeName(type) {
  const nameMap = {
    smoke: '烟雾传感器',
    water: '水浸传感器',
    socket: '智能排风',
    switch: '智慧照明开关',
    door: '门禁状态',
    env: '环境传感器',
    tempHum: '温湿度传感器',
    toxicGas: '有毒气体传感器',
    hydrogenSulfide: '硫化氢传感器',
    sensor: '传感器'
  }
  return nameMap[type] || '传感器'
}

function getLocationName(device, data) {
  return device.deptName || data.deptName || warehouseName.value || '仓库设备'
}

function getSensorSummary(data, type) {
  if (!Object.keys(data).length) {
    return '等待上报'
  }

  if (type === 'water') {
    return `水浸状态：${translateNormalAlarm(data.waterStatus ?? data.water_status ?? data.water)}`
  }

  if (type === 'smoke') {
    return `烟雾状态：${translateSmokeStatus(data.event || data.statusDesc || data.eventDesc || data.concentration)}`
  }

  if (type === 'socket') {
    return `插座状态：${translateSwitch(data.switchStatus ?? data.socket_status ?? data.socketStatus ?? data.status)}`
  }

  if (type === 'switch') {
    const channelParts = [
      data.switch_1 !== undefined && data.switch_1 !== null && data.switch_1 !== '' ? `1路${translateSwitchShort(data.switch_1)}` : null,
      data.switch_2 !== undefined && data.switch_2 !== null && data.switch_2 !== '' ? `2路${translateSwitchShort(data.switch_2)}` : null,
      data.switch_3 !== undefined && data.switch_3 !== null && data.switch_3 !== '' ? `3路${translateSwitchShort(data.switch_3)}` : null
    ].filter(Boolean)

    if (channelParts.length) {
      return `开关状态：${channelParts.join(' / ')}`
    }

    return `开关状态：${translateSwitch(data.switchStatus ?? data.switch_status ?? data.socket_status ?? data.socketStatus ?? data.status)}`
  }

  if (type === 'door') {
    return `门禁状态：${translateSwitch(data.doorStatus ?? data.door_status ?? data.status)}`
  }

  if (type === 'env') {
    const temperature = data.temperature ?? '--'
    const humidity = data.humidity ?? '--'
    return `${temperature}℃ / ${humidity}%`
  }

  if (type === 'tempHum') {
    const temperature = data.temperature ?? '--'
    const humidity = data.humidity ?? '--'
    return `温湿度：${temperature}℃ / ${humidity}%`
  }

  if (type === 'toxicGas') {
    const nh3 = data.nh3 ?? '--'
    return `氨气浓度：${nh3}ppm`
  }

  if (type === 'hydrogenSulfide') {
    const h2s = data.h2s ?? '--'
    return `硫化氢浓度：${h2s}ppm`
  }

  return '数据已上报'
}

function buildMetrics(data, type) {
  if (type === 'tempHum') {
    return buildMetricItems(data, [
      ['temperature', '温度', '℃'],
      ['humidity', '湿度', '%'],
      ['battery', '电量', '%']
    ])
  }

  if (type === 'toxicGas') {
    return buildMetricItems(data, [
      ['nh3', '氨气', 'ppm'],
      ['battery', '电量', '%']
    ])
  }

  if (type === 'hydrogenSulfide') {
    return buildMetricItems(data, [
      ['h2s', '硫化氢', 'ppm'],
      ['battery', '电量', '%']
    ])
  }

  const config = [
    ['temperature', '温度', '℃'],
    ['humidity', '湿度', '%'],
    ['nh3', '氨气', 'ppm'],
    ['h2s', '硫化氢', 'ppm'],
    ['battery', '电量', '%'],
    ['concentration', '烟雾浓度', ''],
    ['voltage', '电压', 'V'],
    ['current', '电流', 'A'],
    ['active_power', '功率', 'W']
  ]

  const metrics = buildMetricItems(data, config)

  if (type === 'water' && (data.waterStatus !== undefined || data.water_status !== undefined || data.water !== undefined)) {
    metrics.unshift({ label: '水浸状态', value: translateNormalAlarm(data.waterStatus ?? data.water_status ?? data.water) })
  }

  if (type === 'socket' && (data.switchStatus !== undefined || data.socket_status !== undefined || data.socketStatus !== undefined || data.status !== undefined)) {
    metrics.unshift({ label: '插座状态', value: translateSwitch(data.switchStatus ?? data.socket_status ?? data.socketStatus ?? data.status) })
  }

  if (type === 'switch') {
    const switchMetrics = [
      data.switch_1 !== undefined && data.switch_1 !== null && data.switch_1 !== '' ? { label: '开关1', value: translateSwitchShort(data.switch_1) } : null,
      data.switch_2 !== undefined && data.switch_2 !== null && data.switch_2 !== '' ? { label: '开关2', value: translateSwitchShort(data.switch_2) } : null,
      data.switch_3 !== undefined && data.switch_3 !== null && data.switch_3 !== '' ? { label: '开关3', value: translateSwitchShort(data.switch_3) } : null
    ].filter(Boolean)

    return [...switchMetrics, ...metrics].slice(0, 6)
  }

  if (type === 'door' && (data.doorStatus !== undefined || data.door_status !== undefined || data.status !== undefined)) {
    metrics.unshift({ label: '门禁状态', value: translateSwitch(data.doorStatus ?? data.door_status ?? data.status) })
  }

  return metrics.slice(0, 6)
}

function buildMetricItems(data, config) {
  return config
    .filter(([key]) => data[key] !== undefined && data[key] !== null && data[key] !== '')
    .map(([key, label, unit]) => ({
      label,
      value: `${data[key]}${unit}`
    }))
}

function translateNormalAlarm(value) {
  const normalized = String(value).toLowerCase()
  if (normalized === '0' || normalized === 'normal' || normalized === 'false') return '正常'
  if (normalized === '1' || normalized === 'alarm' || normalized === 'true') return '告警'
  return value || '正常'
}

function translateSmokeStatus(value) {
  const normalized = String(value ?? '').trim().toLowerCase()
  if (!normalized || normalized === '0' || normalized === 'normal' || normalized === 'removed' || normalized.includes('normal battery')) {
    return '正常'
  }
  if (normalized === '1' || normalized === 'alarm' || normalized.includes('alarm')) {
    return '告警'
  }
  return value || '正常'
}

function translateSwitch(value) {
  const normalized = String(value ?? '').trim().toLowerCase()
  if (normalized === '0' || normalized === 'off' || normalized === 'false' || normalized === 'close' || normalized === 'closed') return '关闭'
  if (normalized === '1' || normalized === 'on' || normalized === 'true' || normalized === 'open' || normalized === 'opened') return '开启'
  return value || '--'
}

function translateSwitchShort(value) {
  const normalized = String(value ?? '').trim().toLowerCase()
  if (normalized === '0' || normalized === 'off' || normalized === 'false' || normalized === 'close' || normalized === 'closed') return '关'
  if (normalized === '1' || normalized === 'on' || normalized === 'true' || normalized === 'open' || normalized === 'opened') return '开'
  return value || '--'
}

function formatTime(value) {
  if (!value) {
    return ''
  }

  const date = new Date(value)
  if (Number.isNaN(date.getTime())) {
    return String(value).replace('T', ' ').slice(0, 19)
  }

  return date.toLocaleString('zh-CN', {
    month: '2-digit',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit'
  })
}

onMounted(() => {
  loadDevices()
  connectWs(handleWsMessage)
})

onBeforeUnmount(() => {
  cameraPlayerRef.value?.destroy?.()
  closeWs(handleWsMessage)
})

watch(cameraDeviceSerial, () => {
  resetCameraState()
  cameraPlayerRef.value?.destroy?.()
  cameraStatusText.value = '点击开始播放摄像头'
})
</script>

<style lang="scss" scoped>
.warehouse-page {
  min-height: calc(100vh - 84px);
  padding: 24px 32px 36px;
  color: #132033;
  background:
    radial-gradient(circle at 10% 0%, rgba(20, 184, 166, 0.18), transparent 28%),
    radial-gradient(circle at 90% 8%, rgba(14, 165, 233, 0.16), transparent 30%),
    linear-gradient(135deg, #eef8f7 0%, #f8fbff 52%, #edf4ff 100%);
}

.warehouse-hero {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 20px;
  align-items: center;
  padding: 24px 28px;
  margin-bottom: 18px;
  color: #fff;
  background:
    linear-gradient(135deg, rgba(8, 47, 73, 0.95), rgba(15, 118, 110, 0.92)),
    radial-gradient(circle at 78% 24%, rgba(125, 211, 252, 0.44), transparent 34%);
  border-radius: 28px;
  box-shadow: 0 24px 60px rgba(8, 47, 73, 0.22);

  p {
    margin: 0 0 8px;
    font-size: 12px;
    font-weight: 900;
    letter-spacing: 0.16em;
    color: rgba(204, 251, 241, 0.86);
    text-transform: uppercase;
  }

  h1 {
    margin: 0;
    font-size: 30px;
    letter-spacing: 0.04em;
  }

  em {
    display: block;
    margin-top: 10px;
    font-size: 13px;
    font-style: normal;
    color: rgba(240, 253, 250, 0.82);
  }
}

.back-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 38px;
  padding: 0 14px;
  color: #ccfbf1;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 999px;

  span {
    width: 9px;
    height: 9px;
    border-bottom: 2px solid currentcolor;
    border-left: 2px solid currentcolor;
    transform: rotate(45deg);
  }
}

.hero-meta {
  min-width: 150px;
  padding: 16px;
  text-align: center;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 22px;

  strong {
    display: block;
    font-size: 28px;
  }

  span {
    font-size: 12px;
    color: rgba(240, 253, 250, 0.78);
  }
}

.summary-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  margin-bottom: 18px;
}

.summary-card {
  padding: 18px;
  background: rgba(255, 255, 255, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.9);
  border-radius: 22px;
  box-shadow: 0 18px 40px rgba(15, 23, 42, 0.08);

  span {
    display: block;
    margin-bottom: 8px;
    font-size: 12px;
    color: #64748b;
  }

  strong {
    font-size: 26px;
    color: #0f172a;
  }

  &.online strong {
    color: #059669;
  }

  &.offline strong {
    color: #475569;
  }
}

.sensor-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
}

.sensor-card {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 250px;
  padding: 20px;
  background:
  linear-gradient(145deg, rgba(255, 255, 255, 0.98), rgba(247, 252, 253, 0.92)),
  radial-gradient(circle at 96% 96%, rgba(20, 184, 166, 0.14), transparent 22%);
  border: 1px solid rgba(221, 235, 238, 0.95);
  border-radius: 28px;
  box-shadow: 0 20px 48px rgba(15, 23, 42, 0.07);
  transition: transform 0.2s ease, box-shadow 0.2s ease;

  &::before {
    position: absolute;
    inset: 0 auto 0 0;
    width: 6px;
    content: "";
    background: linear-gradient(180deg, #14b8a6, #38bdf8);
  }

  &::after {
    position: absolute;
    right: -36px;
    bottom: -46px;
    width: 150px;
    height: 150px;
    pointer-events: none;
    content: "";
    background: rgba(20, 184, 166, 0.08);
    border-radius: 50%;
  }

  &:hover {
    transform: translateY(-4px);
    box-shadow: 0 24px 58px rgba(15, 23, 42, 0.12);
  }

  &.offline {
    filter: grayscale(0.28);

    &::before {
      background: linear-gradient(180deg, #94a3b8, #cbd5e1);
    }
  }
}

.sensor-top,
.sensor-footer {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.sensor-top {
  min-height: 64px;
}

.sensor-content {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(230px, 0.9fr) minmax(260px, 1.1fr);
  gap: 16px;
  align-items: stretch;
  flex: 1;
}

.sensor-main {
  display: flex;
  min-width: 0;
  height: 100%;
  min-height: 238px;
  flex-direction: column;
  padding: 20px;
  background:
    linear-gradient(145deg, rgba(255, 255, 255, 0.88), rgba(240, 253, 250, 0.45));
  border: 1px solid rgba(226, 232, 240, 0.58);
  border-radius: 22px;
}

.sensor-icon {
  display: grid;
  width: 64px;
  height: 64px;
  place-items: center;
  color: #0f766e;
  background:
    linear-gradient(135deg, rgba(204, 251, 241, 0.95), rgba(240, 253, 250, 0.95));
  border: 1px solid rgba(20, 184, 166, 0.24);
  border-radius: 20px;
  box-shadow: inset 0 -10px 18px rgba(15, 118, 110, 0.08);

  span {
    font-size: 22px;
    font-weight: 900;
  }

  &.smoke { color: #ea580c; background: linear-gradient(135deg, #ffedd5, #fff7ed); border-color: #fed7aa; }
  &.water { color: #0284c7; background: linear-gradient(135deg, #e0f2fe, #f0f9ff); border-color: #bae6fd; }
  &.acSocket { color: #2563eb; background: linear-gradient(135deg, #dbeafe, #eff6ff); border-color: #bfdbfe; }
  &.socket { color: #16a34a; background: linear-gradient(135deg, #dcfce7, #f0fdf4); border-color: #bbf7d0; }
  &.switch { color: #ca8a04; background: linear-gradient(135deg, #fef3c7, #fffbeb); border-color: #fcd34d; }
  &.door { color: #475569; background: linear-gradient(135deg, #e2e8f0, #f8fafc); border-color: #cbd5e1; }
  &.env { color: #0d9488; background: linear-gradient(135deg, #ccfbf1, #f0fdfa); border-color: #99f6e4; }
  &.tempHum { color: #0d9488; background: linear-gradient(135deg, #ccfbf1, #f0fdfa); border-color: #99f6e4; }
  &.toxicGas { color: #ca8a04; background: linear-gradient(135deg, #fef9c3, #fffbeb); border-color: #fde68a; }
  &.hydrogenSulfide { color: #7c3aed; background: linear-gradient(135deg, #ede9fe, #faf5ff); border-color: #ddd6fe; }
  &.camera { color: #2563eb; background: linear-gradient(135deg, #dbeafe, #eff6ff); border-color: #bfdbfe; }
}

.sensor-state {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 10px;
  font-size: 12px;
  color: #059669;
  background: rgba(220, 252, 231, 0.86);
  border-radius: 999px;

  i {
    width: 7px;
    height: 7px;
    background: #22c55e;
    border-radius: 50%;
  }
}

.sensor-card.offline .sensor-state {
  color: #64748b;
  background: #f1f5f9;

  i {
    background: #94a3b8;
  }
}

.sensor-body {
  position: relative;
  z-index: 1;
  min-width: 0;
  padding-top: 18px;
  margin-top: 18px;
  text-align: left;
  border-top: 1px solid rgba(226, 232, 240, 0.7);

  p {
    display: inline-flex;
    max-width: 100%;
    padding: 0;
    margin: 0 0 8px;
    overflow: hidden;
    font-size: 13px;
    font-weight: 800;
    letter-spacing: 0.01em;
    color: #0f766e;
    text-overflow: ellipsis;
    white-space: nowrap;
    background: transparent;
    border: 0;
  }

  h2 {
    display: block;
    max-width: 100%;
    padding: 0;
    margin: 0 0 12px;
    overflow: hidden;
    font-size: 22px;
    font-weight: 900;
    line-height: 1.18;
    letter-spacing: -0.02em;
    color: #0f172a;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  strong {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    padding: 7px 11px;
    margin-top: 0;
    overflow: hidden;
    font-size: 13px;
    font-weight: 800;
    line-height: 1.3;
    color: #0f172a;
    text-overflow: ellipsis;
    white-space: nowrap;
    background: rgba(248, 250, 252, 0.88);
    border: 1px solid rgba(226, 232, 240, 0.82);
    border-radius: 999px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.86);
  }

  strong::before {
    width: 7px;
    height: 7px;
    flex: 0 0 auto;
    content: "";
    background: #14b8a6;
    border-radius: 50%;
    box-shadow: 0 0 0 4px rgba(20, 184, 166, 0.1);
  }
}

.socket-actions {
  display: flex;
  gap: 10px;
  margin-top: 14px;
}

.switch-actions {
  display: grid;
  gap: 8px;
  margin-top: 14px;
}

.switch-action-row {
  display: grid;
  grid-template-columns: 48px repeat(2, minmax(0, 1fr));
  gap: 8px;
  align-items: center;
}

.switch-channel-label {
  font-size: 12px;
  font-weight: 800;
  color: #0f766e;
}

.control-btn {
  min-width: 94px;
  padding: 9px 14px;
  font-size: 13px;
  font-weight: 800;
  line-height: 1;
  cursor: pointer;
  border: 1px solid transparent;
  border-radius: 999px;
  transition: transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;

  &:hover:not(:disabled) {
    transform: translateY(-1px);
  }

  &:disabled {
    cursor: not-allowed;
    opacity: 0.68;
  }
}

.control-on {
  color: #065f46;
  background: linear-gradient(135deg, #dcfce7, #f0fdf4);
  border-color: #86efac;
  box-shadow: 0 10px 18px rgba(34, 197, 94, 0.12);
}

.control-off {
  color: #9a3412;
  background: linear-gradient(135deg, #ffedd5, #fff7ed);
  border-color: #fdba74;
  box-shadow: 0 10px 18px rgba(249, 115, 22, 0.12);
}

.metric-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  height: 100%;
  min-height: 238px;
  padding: 18px;
  background:
    linear-gradient(145deg, rgba(255, 255, 255, 0.92), rgba(248, 250, 252, 0.78));
  border: 1px solid rgba(226, 232, 240, 0.72);
  border-radius: 22px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.72);

  > p {
    margin: 0 0 14px;
    font-size: 12px;
    font-weight: 900;
    letter-spacing: 0.12em;
    color: #64748b;
    text-transform: uppercase;
  }
}

.camera-card {
  .sensor-state {
    color: #2563eb;
    background: rgba(219, 234, 254, 0.86);

    i {
      background: #3b82f6;
    }
  }

  &.camera-active .sensor-state {
    color: #059669;
    background: rgba(220, 252, 231, 0.86);

    i {
      background: #22c55e;
    }
  }
}

.camera-panel {
  gap: 12px;
}

.camera-stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 238px;
  overflow: hidden;
  cursor: pointer;
  background:
    linear-gradient(145deg, rgba(15, 23, 42, 0.92), rgba(30, 41, 59, 0.95)),
    radial-gradient(circle at 15% 20%, rgba(59, 130, 246, 0.28), transparent 28%);
  border: 1px solid rgba(148, 163, 184, 0.2);
  border-radius: 18px;
}

.camera-placeholder {
  display: grid;
  gap: 8px;
  padding: 0 18px;
  text-align: center;

  strong {
    font-size: 18px;
    color: #f8fafc;
  }

  span {
    font-size: 12px;
    color: rgba(226, 232, 240, 0.78);
  }
}

.camera-video {
  width: 100%;
  height: 100%;
  min-height: 238px;
  object-fit: cover;
  background: #020617;
}

.camera-expire,
.camera-error {
  font-size: 12px;
  line-height: 1.4;
}

.camera-expire {
  color: #64748b;
}

.camera-error {
  color: #dc2626;
}

.metric-list {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  margin-top: auto;

  span {
    display: grid;
    min-height: 62px;
    padding: 11px 12px;
    color: #0f172a;
    background:
      linear-gradient(145deg, rgba(240, 253, 250, 0.88), rgba(255, 255, 255, 0.82));
    border: 1px solid rgba(153, 246, 228, 0.56);
    border-radius: 18px;

    em {
      font-size: 11px;
      font-style: normal;
      color: #64748b;
    }

    strong {
      margin-top: 4px;
      overflow: hidden;
      font-size: 17px;
      font-weight: 900;
      color: #0f766e;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }

  .muted {
    color: #94a3b8;
    background: #f8fafc;
    border-color: #e2e8f0;
  }
}

.sensor-footer {
  position: relative;
  z-index: 1;
  gap: 12px;
  padding: 10px 16px 0;
  margin-top: 14px;
  font-size: 12px;
  color: #94a3b8;
  border-top: 1px dashed #e2e8f0;
}

.empty-state {
  display: grid;
  min-height: 240px;
  grid-column: 1 / -1;
  color: #94a3b8;
  place-items: center;
  background: rgba(255, 255, 255, 0.78);
  border: 1px dashed #cbd5e1;
  border-radius: 24px;
}

@media (max-width: 1200px) {
  .summary-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .sensor-content {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 900px) {
  .warehouse-page {
    padding: 16px;
  }

  .warehouse-hero {
    grid-template-columns: 1fr;
  }

  .sensor-grid,
  .summary-grid {
    grid-template-columns: 1fr;
  }
}
</style>
