<template>
  <ContentWrap class="device-data-page">
    <div class="page-header" style="display: flex; justify-content: flex-start; margin-bottom: 16px;">
      <el-button type="primary" @click="openBindDialog">
        <el-icon class="mr-5px"><Plus /></el-icon>
        绑定设备
      </el-button>
    </div>
    <el-divider />
    <div class="device-card-list" v-loading="loading">
      <el-empty v-if="!loading && deviceCards.length === 0" description="暂无监测设备" />
      <div v-else class="device-card-grid">
        <el-card
          v-for="item in deviceCards"
          :key="item.id"
          class="device-card"
          shadow="hover"
          @click="openHistoryDialog(item)"
        >
          <div class="device-card-header">
            <div>
              <div class="device-card-name">{{ item.name || '未命名设备' }}</div>
            </div>
            <el-button
              class="card-action"
              text
              type="danger"
              :disabled="!item.deviceId"
              @click.stop="handleUnbindDevice(item)"
            >
              <el-icon><Delete /></el-icon>
            </el-button>
          </div>
          <div class="device-card-meta meta-secondary">
            <span>设备类型：{{ deviceTypeText(item.latestData?.deviceType) }}</span>
          </div>

          <div class="metric-group">
            <div
              :class="[
                'metric-item',
                'metric-temperature',
                { 'metric-abnormal': isTemperatureAbnormal(item), 'metric-normal': isTemperatureNormal(item) }
              ]"
            >
              <div class="metric-label">温度(℃)</div>
              <div class="metric-value">{{ formatMetric(item.latestData?.temperatureC, 2) }}</div>
            </div>
            <div
              :class="[
                'metric-item',
                'metric-humidity',
                { 'metric-abnormal': isHumidityAbnormal(item), 'metric-normal': isHumidityNormal(item) }
              ]"
            >
              <div class="metric-label">湿度(RH)</div>
              <div class="metric-value">{{ formatMetric(item.latestData?.analog5, 2) }}</div>
            </div>
          </div>

          <div class="device-card-footer">
            <div>
              <span class="footer-label">采集时间：</span>
              <span>{{ formatTimestamp(item.latestData?.collectedAt) }}</span>
            </div>
          </div>
        </el-card>
      </div>
    </div>
  </ContentWrap>

  <DeviceDataForm ref="formRef" />

  <!-- 绑定设备弹窗 -->
  <el-dialog v-model="bindDialogVisible" title="绑定设备到产品" width="500px" destroy-on-close>
    <el-form ref="bindFormRef" :model="bindForm" :rules="bindFormRules" label-width="100px">
      <el-form-item label="选择产品" prop="productId">
        <el-select v-model="bindForm.productId" placeholder="请选择产品" filterable class="!w-full">
          <el-option
            v-for="item in productList"
            :key="item.id"
            :label="item.name"
            :value="item.id"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="设备ID" prop="deviceId">
        <el-input-number
          v-model="bindForm.deviceId"
          placeholder="请输入设备ID"
          :min="1"
          class="!w-full"
        />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="bindDialogVisible = false">取消</el-button>
      <el-button type="primary" @click="handleBindDevice" :loading="bindLoading">确定</el-button>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { Delete, Plus } from '@element-plus/icons-vue'
import dayjs from 'dayjs'

import DeviceDataForm from './DeviceDataForm.vue'
import { ProductApi } from '@/api/iot/product/product/index'

interface LatestData {
  collectedAt?: number | string | null
  temperatureC?: number | string | null
  analog5?: number | string | null
  currentValue?: number | string | null
  counter1?: number | string | null
  counter2?: number | string | null
  counter1Total?: number | string | null
  counter2Total?: number | string | null
  powerW?: number | string | null
  isOn?: boolean
  deviceType?: number | string | null
  frameRaw?: string | null
}

interface ProductWithLatest {
  id: number
  deviceId?: number
  name?: string
  productKey?: string
  categoryName?: string | null
  netType?: number | null
  status?: number | null
  latestData?: LatestData | null
  tempLimit?: string | null
  humidityLimit?: string | null
}

const formRef = ref<InstanceType<typeof DeviceDataForm> | null>(null)
const loading = ref(false)
const deviceCards = ref<ProductWithLatest[]>([])

// 绑定设备相关
const bindDialogVisible = ref(false)
const bindFormRef = ref<FormInstance>()
const bindLoading = ref(false)
const productList = ref<Array<{ id: number; name: string }>>([])
const bindForm = ref({
  productId: undefined as number | undefined,
  deviceId: undefined as number | undefined
})

const bindFormRules: FormRules = {
  productId: [{ required: true, message: '请选择产品', trigger: 'change' }],
  deviceId: [{ required: true, message: '请输入设备ID', trigger: 'blur' }]
}

const fetchDeviceCards = async () => {
  loading.value = true
  try {
    const res = await ProductApi.getMonitoredProductList()
    const data = (res as any)?.data ?? (res as any) ?? []
    deviceCards.value = data
  } catch (error) {
    deviceCards.value = []
    ElMessage.error('获取设备列表失败')
  } finally {
    loading.value = false
  }
}

const formatMetric = (value: number | string | null | undefined, digits = 2) => {
  if (value === null || value === undefined) return '-'
  const num = typeof value === 'number' ? value : Number(value)
  if (Number.isNaN(num)) return '-'
  return digits >= 0 ? num.toFixed(digits) : String(num)
}

const formatTimestamp = (value: number | string | null | undefined) => {
  if (value === null || value === undefined) return '-'
  const num = typeof value === 'number' ? value : Number(value)
  const ts = Number.isNaN(num) ? Date.parse(String(value)) : num < 1e12 ? num * 1000 : num
  if (!Number.isFinite(ts)) return '-'
  const date = dayjs(ts)
  return date.isValid() ? date.format('YYYY-MM-DD HH:mm:ss') : '-'
}

const parseLimitRange = (limit?: string | null) => {
  if (!limit) return null
  const [minStr, maxStr] = limit.split(',').map((item) => item?.trim())
  const min = Number(minStr)
  const max = Number(maxStr)
  if (!Number.isFinite(min) || !Number.isFinite(max)) return null
  return { min, max }
}

const isValueOutOfRange = (value: number | string | null | undefined, limit?: string | null) => {
  const range = parseLimitRange(limit)
  if (!range) return false
  if (value === null || value === undefined) return false
  const num = typeof value === 'number' ? value : Number(value)
  if (!Number.isFinite(num)) return false
  return num < range.min || num > range.max
}

const isTemperatureAbnormal = (item: ProductWithLatest) =>
  isValueOutOfRange(item.latestData?.temperatureC, item.tempLimit)

const isHumidityAbnormal = (item: ProductWithLatest) =>
  isValueOutOfRange(item.latestData?.analog5, item.humidityLimit)

const isTemperatureNormal = (item: ProductWithLatest) => {
  const range = parseLimitRange(item.tempLimit)
  if (!range) return false
  if (item.latestData?.temperatureC === null || item.latestData?.temperatureC === undefined) return false
  const num = typeof item.latestData.temperatureC === 'number' ? item.latestData.temperatureC : Number(item.latestData.temperatureC)
  if (!Number.isFinite(num)) return false
  return num >= range.min && num <= range.max
}

const isHumidityNormal = (item: ProductWithLatest) => {
  const range = parseLimitRange(item.humidityLimit)
  if (!range) return false
  if (item.latestData?.analog5 === null || item.latestData?.analog5 === undefined) return false
  const num = typeof item.latestData.analog5 === 'number' ? item.latestData.analog5 : Number(item.latestData.analog5)
  if (!Number.isFinite(num)) return false
  return num >= range.min && num <= range.max
}

const deviceTypeText = (type?: number | string | null) => {
  if (type === null || type === undefined) return '未知'
  if (typeof type === 'number') {
    const map: Record<number, string> = {
      0: '直连设备',
      1: '网关子设备',
      2: '网关设备'
    }
    return map[type] || `类型${type}`
  }
  const trimmed = String(type).trim()
  return trimmed.length > 0 ? trimmed : '未知'
}

const openHistoryDialog = (device: ProductWithLatest) => {
  formRef.value?.openHistoryDialog(device)
}

// 打开绑定设备弹窗
const openBindDialog = async () => {
  bindForm.value = {
    productId: undefined,
    deviceId: undefined
  }
  bindFormRef.value?.resetFields()
  bindDialogVisible.value = true
  await fetchProductList()
}

// 获取产品列表
const fetchProductList = async () => {
  try {
    const res = await ProductApi.getSimpleProductList()
    const data = (res as any)?.data ?? (res as any) ?? []
    productList.value = data.map((item: any) => ({
      id: item.id,
      name: item.name || `产品${item.id}`
    }))
  } catch (error) {
    ElMessage.error('获取产品列表失败')
    productList.value = []
  }
}

// 绑定设备
const handleBindDevice = async () => {
  if (!bindFormRef.value) return
  await bindFormRef.value.validate(async (valid) => {
    if (!valid) return
    bindLoading.value = true
    try {
      // 先获取产品详情
      const productRes = await ProductApi.getProduct(bindForm.value.productId!)
      const productData = (productRes as any)?.data ?? (productRes as any)

      // 更新产品的 deviceId
      const updateData = {
        ...productData,
        deviceId: bindForm.value.deviceId
      }

      await ProductApi.updateProduct(updateData)
      ElMessage.success('绑定设备成功')
      bindDialogVisible.value = false
      // 刷新设备卡片列表
      await fetchDeviceCards()
    } catch (error) {
      ElMessage.error('绑定设备失败')
    } finally {
      bindLoading.value = false
    }
  })
}

const handleUnbindDevice = async (product: ProductWithLatest) => {
  if (!product.deviceId) {
    ElMessage.info('当前产品未绑定设备')
    return
  }
  try {
    await ElMessageBox.confirm(`确认解除产品「${product.name || product.id}」与设备的绑定？`, '提示', {
      type: 'warning'
    })
  } catch (error) {
    return
  }
  try {
    const productRes = await ProductApi.getProduct(product.id)
    const productData = (productRes as any)?.data ?? (productRes as any)
    await ProductApi.updateProduct({
      ...productData,
      deviceId: null
    })
    ElMessage.success('解绑成功')
    await fetchDeviceCards()
  } catch (error) {
    ElMessage.error('解绑失败')
  }
}

onMounted(fetchDeviceCards)
</script>

<style scoped>
.device-data-page {
  min-height: 400px;
}

.page-header {
  margin-bottom: 16px;
  display: flex;
  justify-content: flex-end;
}

.device-card-list {
  min-height: 320px;
}

.device-card-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}

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

@media (max-width: 900px) {
  .device-card-grid {
    grid-template-columns: repeat(1, minmax(0, 1fr));
  }
}

.device-card {
  border-radius: 12px;
  cursor: pointer;
  transition: transform 0.2s;
}

.device-card:hover {
  transform: translateY(-2px);
}

.device-card-header {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.device-card-name {
  font-size: 18px;
  font-weight: 600;
  color: #303133;
}

.device-card-sub {
  margin-top: 6px;
  font-size: 12px;
  color: #909399;
}

.device-card-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #909399;
  margin-bottom: 12px;
}

.meta-secondary {
  justify-content: flex-start;
  gap: 20px;
  margin-top: -4px;
  margin-bottom: 12px;
}

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

.metric-item {
  background-color: #f5f7fa;
  border-radius: 8px;
  padding: 10px 12px;
}

.metric-abnormal {
  background-color: #ffecec;
}

.metric-normal {
  background-color: #ddffe7;
}

.metric-label {
  font-size: 12px;
  color: #909399;
}

.metric-value {
  margin-top: 4px;
  font-size: 20px;
  font-weight: 600;
  color: #303133;
}

.metric-desc {
  margin-top: 2px;
  font-size: 12px;
  color: #909399;
}

.device-card-footer {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid #ebeef5;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  color: #909399;
}

.footer-label {
  color: #606266;
}

.card-action {
  padding: 0;
  min-height: auto;
}

.card-action[disabled] {
  color: #dcdfe6;
}

.frame-text {
  display: inline-block;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
</style>
