<template>
  <view class="page-container">
    <!-- 简洁标签栏 -->
    <view class="tab-container">
      <view
        v-for="item in tabData"
        :key="item.value"
        @click="currentTab = item.value"
        class="tab-item"
        :class="{ 'tab-active': currentTab === item.value }"
      >
        <text class="tab-label">{{ item.text }}</text>
        <view class="tab-indicator" v-if="currentTab === item.value"></view>
      </view>
    </view>

    <!-- 刷新按钮 -->
    <view class="refresh-btn-fixed" @click="refreshAll">
      <text class="refresh-icon" :class="{ rotating: isRefreshing }">⟳</text>
    </view>

    <scroll-view scroll-y class="scroll-container" v-if="currentTab === 1" @scroll="onScroll">
      <!-- 顶部状态栏 -->
      <view class="status-bar">
        <view class="status-item">
          <text class="status-label">Socket</text>
          <text class="status-value" :class="isConnected ? 'status-通畅' : 'status-异常'">{{ isConnected ? '已连接' : '未连接' }}</text>
        </view>
        <view class="status-divider"></view>
        <view class="status-item">
          <text class="status-label">更新时间</text>
          <text class="status-value status-time">{{ lastUpdateTime }}</text>
        </view>
        <view class="status-divider"></view>
        <view class="status-item">
          <text class="status-label">设备数</text>
          <text class="status-value">{{ deviceDefs.length }}</text>
        </view>
      </view>



      <view v-for="dev in deviceDefs" :key="'device_' + dev.deviceCode" :id="'device-' + dev.deviceCode" class="device-section">
        <view class="section-title">{{ dev.desc }}（{{ dev.deviceCode }}）</view>

        <view v-if="fieldTrendMap[dev.deviceCode]">
          <view v-for="fieldName in (dev.paramFields || [])" :key="dev.deviceCode + '_' + fieldName" class="field-chart-container">
            <view v-if="fieldTrendMap[dev.deviceCode][fieldName]">
              <view class="chart-header">
                <text class="chart-title">{{ getFieldLabel(fieldName) }}</text>
                <text class="chart-unit">{{ getFieldUnit(fieldName) }}</text>
              </view>

              <view class="chart-box">
                <qiun-data-charts type="line" :chartData="toLineChart(fieldTrendMap[dev.deviceCode][fieldName], fieldName)" :opts="lineChartOpts" />
              </view>

              <view class="stats-grid">
                <view class="stat-item"><text class="stat-label">AVG</text><text class="stat-value">{{ formatNum(fieldTrendMap[dev.deviceCode][fieldName].avg) }}</text></view>
                <view class="stat-item"><text class="stat-label">MAX</text><text class="stat-value">{{ formatNum(fieldTrendMap[dev.deviceCode][fieldName].max) }}</text></view>
                <view class="stat-item"><text class="stat-label">MIN</text><text class="stat-value">{{ formatNum(fieldTrendMap[dev.deviceCode][fieldName].min) }}</text></view>
                <view class="stat-item"><text class="stat-label">LAST</text><text class="stat-value">{{ formatNum(fieldTrendMap[dev.deviceCode][fieldName].last) }}</text></view>
              </view>
            </view>

            <view v-else class="placeholder-box">
              <text class="placeholder-text">等待 {{ getFieldLabel(fieldName) }} 数据...</text>
            </view>
          </view>
        </view>

        <view v-else class="placeholder-box">
          <text class="placeholder-text">等待 {{ dev.desc }} 数据...（滚动到此处加载）</text>
        </view>
      </view>
    </scroll-view>
  </view>
</template>

<script>
import { listDeviceEnumAll } from '@/api/pocket/deviceEnum'
import { getDeviceFieldMetaAll } from '@/api/pocket/deviceFieldMeta'
import { createMeasureSocket } from '@/utils/socketMeasure'

export default {
  data() {
    return {
      currentTab: 1,
      tabData: [
        { text: '实时监控', value: 1 }
      ],
      isRefreshing: false,
      isConnected: false,
      lastUpdateTime: '—',

      // socket最新消息
      latestMeasure: null,

      // 设备定义（后端 DeviceEnum 全量）
      deviceDefs: [],

      // 字段元数据：fieldName -> {label, unit, description}
      fieldMeta: {},

      // device_field_trend: deviceCode -> { fieldName -> trendDTO }
      fieldTrendMap: {},

      // 懒加载：已订阅的设备列表
      subscribedDeviceCodes: [],

      lineChartOpts: {
        color: ['#0066cc', '#409eff', '#66b1ff', '#a0cfff', '#d9ecff', '#ecf5ff'],
        padding: [15, 15, 0, 15],
        enableScroll: false,
        legend: { show: true, position: 'top', fontSize: 10, lineHeight: 14, itemGap: 6 },
        dataLabel: false,
        dataPointShape: false,
        xAxis: { disableGrid: true, rotateLabel: true, itemCount: 5, labelCount: 5, fontSize: 10 },
        yAxis: {
          gridType: 'dash',
          dashLength: 4,
          gridColor: '#e4e7ed',
          showTitle: true,
          fontSize: 10,
          data: [{ title: '数值' }]
        },
        extra: { line: { type: 'curve', width: 2, activeType: 'hollow' } }
      },

      socketClient: null,
      fieldTrendSocket: null,
      fieldTrendSubscribed: false,
      subscribedDeviceCodes: []
    }
  },

  mounted() {
    this.initSocket()

    Promise.all([this.loadDeviceDefs(), this.loadFieldMeta()]).then(() => {
      // 页面初始化完成后，默认订阅首屏设备（懒加载）
      this.$nextTick(() => {
        this.updateVisibleDeviceSubscriptions()
      })
    })

    this.updateLastTime()
  },

  beforeDestroy() {
    if (this.socketClient) this.socketClient.close()
    if (this.fieldTrendSocket) this.fieldTrendSocket.close()
  },

  methods: {
    async loadDeviceDefs() {
      const res = await listDeviceEnumAll()
      this.deviceDefs = (res && res.data) || []


    },

    async loadFieldMeta() {
      const res = await getDeviceFieldMetaAll()
      this.fieldMeta = (res && res.data) || {}
    },

    initSocket() {
      // 实时测量数据 WebSocket
      this.socketClient = createMeasureSocket({
        type: 'track_measure',
        onOpen: () => {
					console.log('初始化socket成功')
          this.isConnected = true
        },
        onClose: () => {
          this.isConnected = false
        },
        onError: () => {
          this.isConnected = false
        },
        onMessage: (data) => {
          try {
            this.latestMeasure = JSON.parse(data)
            this.updateLastTime()
          } catch (e) {
            // ignore
          }
        }
      })

      this.socketClient.connect()

      // 点位趋势 WebSocket（懒加载订阅）
      this.fieldTrendSocket = createMeasureSocket({
        type: 'device_field_trend',
        onOpen: () => {
          console.log('点位趋势 WebSocket 连接成功')
          // 初次连接后先不订阅，等滚动/展开触发
          this.fieldTrendSubscribed = true
          this.sendFieldTrendSubscribe([])
        },
        onClose: () => {
          console.log('点位趋势 WebSocket 连接关闭')
          this.fieldTrendSubscribed = false
        },
        onError: () => {
          console.error('点位趋势 WebSocket 连接错误')
          this.fieldTrendSubscribed = false
        },
        onMessage: (data) => {
          try {
            const trend = JSON.parse(data)
            if (!trend || !trend.deviceCode || !trend.fieldName) return
            if (!this.fieldTrendMap[trend.deviceCode]) {
              this.$set(this.fieldTrendMap, trend.deviceCode, {})
            }
            this.$set(this.fieldTrendMap[trend.deviceCode], trend.fieldName, trend)
          } catch (e) {
            console.error('解析点位趋势数据失败:', e)
          }
        }
      })

      this.fieldTrendSocket.connect()
    },

    // 滚动事件：节流触发订阅更新
    onScroll() {
      if (this._scrollTimer) return
      this._scrollTimer = setTimeout(() => {
        this._scrollTimer = null
        this.updateVisibleDeviceSubscriptions()
      }, 200)
    },

    // 计算当前可视区域内的设备，并向后端发送订阅（懒加载）
    updateVisibleDeviceSubscriptions() {
      const list = this.deviceDefs || []
      if (!list.length) return

      // 使用 selectorQuery 获取每个设备块的位置
      const query = uni.createSelectorQuery().in(this)
      list.forEach((d) => {
        query.select('#device-' + d.deviceCode).boundingClientRect()
      })
      query.selectViewport().scrollOffset()

      query.exec((res) => {
        // res: [rect1, rect2, ..., viewport]
        if (!res || res.length < 2) return
        const viewport = res[res.length - 1] || {}
        const scrollTop = viewport.scrollTop || 0
        const windowHeight = viewport.windowHeight || 0

        // 预加载阈值：提前 0.5 屏开始订阅
        const preload = windowHeight * 0.5
        const bottom = scrollTop + windowHeight + preload

        // 找到“已经进入/即将进入”的最大索引
        let maxIdx = -1
        for (let i = 0; i < list.length; i++) {
          const rect = res[i]
          if (!rect) continue
          const top = rect.top + scrollTop
          if (top <= bottom) {
            maxIdx = i
          }
        }

        // 订阅规则：当前可视（含预加载）位置的设备及其以前全部设备
        const nextList = maxIdx >= 0 ? list.slice(0, maxIdx + 1).map((d) => d.deviceCode) : []

        if (JSON.stringify(nextList) === JSON.stringify(this.subscribedDeviceCodes || [])) return
        this.subscribedDeviceCodes = nextList
        this.sendFieldTrendSubscribe(nextList)
      })
    },



    getFieldLabel(field) {
      const meta = this.fieldMeta[field]
      return (meta && meta.label) || field
    },

    getFieldUnit(field) {
      const meta = this.fieldMeta[field]
      return (meta && meta.unit) || ''
    },

    sendFieldTrendSubscribe(deviceCodes) {
      if (!this.fieldTrendSocket || !this.fieldTrendSubscribed) return
      this.fieldTrendSocket.send({
        lazy: true,
        deviceCodes: deviceCodes || []
      })
    },

    // 折线图数据转换（仅结构转换，不做计算）
    toLineChart(trend, fieldName) {
      if (!trend) return { categories: [], series: [] }
      return {
        categories: trend.categories || [],
        series: [
          {
            name: this.getFieldLabel(fieldName),
            data: trend.data || []
          }
        ]
      }
    },

    formatNum(v) {
      if (v === null || v === undefined || Number.isNaN(Number(v))) return '—'
      return Number(v).toFixed(2)
    },

    // 根据 DeviceEnum.sourceType 决定取哪个 message
    pickSourceMsg(dev) {
      const m = this.latestMeasure || {}
      if (!dev || !dev.sourceType) return {}
      switch (dev.sourceType) {
        case 'ENTRY':
          return m.appMeasureEntryMessage || {}
        case 'FURNACE':
          return m.appMeasureFurnaceMessage || {}
        case 'COAT':
          return m.appMeasureCoatMessage || {}
        case 'EXIT':
          return m.appMeasureExitMessage || {}
        default:
          return {}
      }
    },

    getRealtimeFieldValue(dev, field) {
      const msg = this.pickSourceMsg(dev)
      const v = msg ? msg[field] : null
      return this.formatValue(v)
    },



    async refreshAll() {
      if (this.isRefreshing) return
      this.isRefreshing = true
      try {
        await Promise.all([this.loadDeviceDefs(), this.loadFieldMeta()])
        // 刷新后更新订阅（按滚动懒加载逻辑）
        this.$nextTick(() => {
          this.updateVisibleDeviceSubscriptions()
        })
        this.updateLastTime()
      } finally {
        this.isRefreshing = false
      }
    },

    updateLastTime() {
      const now = new Date()
      const hour = String(now.getHours()).padStart(2, '0')
      const minute = String(now.getMinutes()).padStart(2, '0')
      const second = String(now.getSeconds()).padStart(2, '0')
      this.lastUpdateTime = `${hour}:${minute}:${second}`
    },

    formatTime(str) {
      if (!str) return ''
      const d = new Date(str)
      const h = String(d.getHours()).padStart(2, '0')
      const m = String(d.getMinutes()).padStart(2, '0')
      return `${h}:${m}`
    },

    formatValue(v) {
      if (v === null || v === undefined || v === '') return '—'
      const n = Number(v)
      if (Number.isNaN(n)) return '—'
      return n.toFixed(2)
    },

  }
}
</script>

<style scoped lang="scss">
.page-container {
  min-height: 100vh;
  background: #f5f7fa;
}

.tab-container {
  display: flex;
  background: #fff;
  border-bottom: 2rpx solid #e4e7ed;
}

.tab-item {
  flex: 1;
  text-align: center;
  padding: 28rpx 0;
  position: relative;

  .tab-label {
    font-size: 28rpx;
    color: #606266;
    font-weight: 400;
  }

  &.tab-active {
    .tab-label {
      color: #0066cc;
      font-weight: 500;
    }
  }

  .tab-indicator {
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 60rpx;
    height: 3rpx;
    background: #0066cc;
  }
}

.refresh-btn-fixed {
  position: fixed;
  right: 32rpx;
  bottom: 120rpx;
  width: 96rpx;
  height: 96rpx;
  background: #0066cc;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 8rpx 20rpx rgba(0, 102, 204, 0.4);
  z-index: 999;

  &:active {
    opacity: 0.8;
    transform: scale(0.95);
  }
}

.refresh-icon {
  font-size: 48rpx;
  color: #fff;
  display: block;
  line-height: 1;

  &.rotating {
    animation: rotate 1s linear infinite;
  }
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.scroll-container {
  height: calc(100vh - 96rpx);
  padding: 24rpx;
}

.status-bar {
  display: flex;
  align-items: center;
  background: #fff;
  padding: 24rpx 32rpx;
  margin-bottom: 24rpx;
  border-radius: 8rpx;
  border: 1rpx solid #e4e7ed;
}

.status-item {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16rpx;
}

.status-label {
  font-size: 26rpx;
  color: #909399;
}

.status-value {
  font-size: 28rpx;
  font-weight: 500;
  color: #303133;

  &.status-通畅 {
    color: #67c23a;
  }

  &.status-异常 {
    color: #f56c6c;
  }

  &.status-time {
    color: #909399;
    font-size: 24rpx;
  }
}

.status-divider {
  width: 1rpx;
  height: 40rpx;
  background: #e4e7ed;
}

.section {
  margin-bottom: 24rpx;
}

.section-title {
  font-size: 30rpx;
  font-weight: 500;
  color: #303133;
  margin-bottom: 20rpx;
  padding-left: 16rpx;
  border-left: 4rpx solid #0066cc;
}

.device-card {
  background: #fff;
  border: 1rpx solid #e4e7ed;
  border-radius: 12rpx;
  padding: 20rpx;
  margin-bottom: 18rpx;
}

.device-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12rpx;
}

.device-header-left {
  display: flex;
  flex-direction: column;
}

.device-title {
  font-size: 30rpx;
  font-weight: 600;
  color: #303133;
}

.device-sub {
  font-size: 22rpx;
  color: #909399;
  margin-top: 6rpx;
}

.device-toggle {
  font-size: 24rpx;
  color: #0066cc;
  font-weight: 500;
}

.metrics-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16rpx;
}

.metric-box {
  background: #fff;
  border: 1rpx solid #e4e7ed;
  border-radius: 8rpx;
  padding: 18rpx 12rpx;
  text-align: center;
}

.metric-name {
  display: block;
  font-size: 22rpx;
  color: #909399;
  margin-bottom: 12rpx;
}

.metric-value {
  display: block;
  font-size: 44rpx;
  font-weight: 600;
  color: #0066cc;
  margin-bottom: 8rpx;
  line-height: 1;

  &.small {
    font-size: 34rpx;
  }
}

.metric-unit {
  display: block;
  font-size: 20rpx;
  color: #909399;
}

.device-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 14rpx;
}

.action-btn {
  padding: 12rpx 18rpx;
  border: 2rpx solid #0066cc;
  border-radius: 8rpx;
}

.action-text {
  font-size: 24rpx;
  color: #0066cc;
  font-weight: 500;
}

.history-tip {
  font-size: 24rpx;
  color: #909399;
  margin-bottom: 12rpx;
}

.history-strong {
  color: #303133;
  font-weight: 600;
}

.field-selector {
  display: flex;
  flex-wrap: wrap;
  gap: 12rpx;
  align-items: center;
  margin-bottom: 12rpx;
}

.field-chip {
  padding: 10rpx 14rpx;
  border-radius: 999rpx;
  border: 2rpx solid #dcdfe6;
  background: #fff;
}

.field-chip.active {
  border-color: #0066cc;
  background: #f0f9ff;
}

.chip-text {
  font-size: 22rpx;
  color: #606266;
}

.field-chip.active .chip-text {
  color: #0066cc;
  font-weight: 500;
}

.field-tip {
  font-size: 22rpx;
  color: #909399;
  margin-left: auto;
}

.chart-box {
  background: #fff;
  border: 1rpx solid #e4e7ed;
  border-radius: 8rpx;
  padding: 24rpx 16rpx;
}

.raw-box {
  background: #fff;
  border: 1rpx solid #e4e7ed;
  border-radius: 8rpx;
  padding: 24rpx 16rpx;
}

.overview-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16rpx;
}

.overview-card {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 12rpx;
  padding: 32rpx 24rpx;
  text-align: center;
  box-shadow: 0 4rpx 12rpx rgba(102, 126, 234, 0.3);
}

.overview-card:nth-child(2) {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  box-shadow: 0 4rpx 12rpx rgba(245, 87, 108, 0.3);
}

.overview-card:nth-child(3) {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
  box-shadow: 0 4rpx 12rpx rgba(79, 172, 254, 0.3);
}

.overview-label {
  display: block;
  font-size: 24rpx;
  color: rgba(255, 255, 255, 0.9);
  margin-bottom: 16rpx;
}

.overview-value {
  display: block;
  font-size: 48rpx;
  font-weight: 700;
  color: #fff;
  line-height: 1.2;
}

.overview-unit {
  display: block;
  font-size: 22rpx;
  color: rgba(255, 255, 255, 0.8);
  margin-top: 8rpx;
}

.status-distribution {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16rpx;
}

.status-item-card {
  background: #fff;
  border: 1rpx solid #e4e7ed;
  border-radius: 12rpx;
  padding: 24rpx;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.status-type {
  font-size: 28rpx;
  color: #606266;
  font-weight: 500;
}

.status-count {
  font-size: 36rpx;
  font-weight: 600;
  color: #0066cc;
}

.stats-table {
  background: #fff;
  border: 1rpx solid #e4e7ed;
  border-radius: 8rpx;
  overflow: hidden;
}

.stats-header {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr 1fr;
  background: #f5f7fa;
  padding: 20rpx 16rpx;
  border-bottom: 1rpx solid #e4e7ed;
}

.stats-row {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr 1fr;
  padding: 20rpx 16rpx;
  border-bottom: 1rpx solid #e4e7ed;
}

.stats-row:last-child {
  border-bottom: none;
}

.stats-col {
  font-size: 24rpx;
  color: #606266;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stats-header .stats-col {
  font-weight: 600;
  color: #303133;
}
</style>
