<script setup lang="ts">
import { Odometer, CircleCheckFilled, CircleCloseFilled } from '@element-plus/icons-vue'
import { IotDeviceApi } from '@/api/pms/device'
import dayjs from 'dayjs'
import { rangeShortcuts } from '@/utils/formatTime'
import { IotStatApi, cancelAllRequests } from '@/api/pms/stat'

import * as echarts from 'echarts'
import { colors } from './color'

const { query } = useRoute()

const data = ref({
  deviceCode: query.code || '',
  deviceName: query.name || '',
  lastInlineTime: query.time || '',
  ifInline: query.ifInline || '',
  dept: query.dept || '',
  vehicle: query.vehicle || '',
  carOnline: query.carOnline || ''
})

interface Dimensions {
  identifier: string
  name: string
  value: string
  color?: string
  response?: boolean
}

const dimensions = ref<Dimensions[]>([])
const gatewayDimensions = ref<Dimensions[]>([])
const carDimensions = ref<Dimensions[]>([])

const disabledDimensions = ref<string[]>(['online', 'vehicle_name'])

interface SelectedDimension {
  [key: Dimensions['name']]: boolean
}

const selectedDimension = ref<SelectedDimension>({})

const dimensionLoading = ref(false)

const disabledDimension = computed(() => (identifier: string) => {
  const response = dimensions.value.find((item) => item.identifier === identifier)?.response

  return { disabled: disabledDimensions.value.includes(identifier) || response, loading: response }
})

async function loadDimensions() {
  if (!query.id) return

  dimensionLoading.value = true

  const gateway = (((await IotDeviceApi.getIotDeviceTds(Number(query.id))) as any[]) ?? [])
    .sort((a, b) => b.modelOrder - a.modelOrder)
    .map((item) => ({
      identifier: item.identifier,
      name: item.modelName,
      value: item.value
    }))
  const car = (((await IotDeviceApi.getIotDeviceZHBDTds(Number(query.id))) as any[]) ?? [])
    .sort((a, b) => b.modelOrder - a.modelOrder)
    .map((item) => ({
      identifier: item.identifier,
      name: item.modelName,
      value: item.value
    }))

  dimensions.value = [...gateway, ...car]
    .filter((item) => !disabledDimensions.value.includes(item.identifier))
    .map((item, index) => ({
      ...item,
      color: colors[index]
    }))

  gatewayDimensions.value = gateway
  carDimensions.value = car

  selectedDimension.value = Object.fromEntries(dimensions.value.map((item) => [item.name, false]))

  selectedDimension.value[dimensions.value[0].name] = true

  dimensionLoading.value = false
}

async function updateDimensionValues() {
  if (!query.id) return

  try {
    // 1. 并行获取最新数据
    const [gatewayRes, carRes] = await Promise.all([
      IotDeviceApi.getIotDeviceTds(Number(query.id)),
      IotDeviceApi.getIotDeviceZHBDTds(Number(query.id))
    ])

    // 2. 创建一个 Map 用于快速查找 (Identifier -> Value)
    // 这样可以将复杂度从 O(N*M) 降低到 O(N)
    const newValueMap = new Map<string, any>()

    const addToMap = (data: any[]) => {
      if (!data) return
      data.forEach((item) => {
        if (item.identifier) {
          newValueMap.set(item.identifier, item.value)
        }
      })
    }

    addToMap(gatewayRes as any[])
    addToMap(carRes as any[])

    // 3. 更新 dimensions.value (保留了之前的 color 和其他属性)
    dimensions.value.forEach((item) => {
      if (newValueMap.has(item.identifier)) {
        item.value = newValueMap.get(item.identifier)
      }
    })

    // 4. 如果还需要同步更新 gatewayDimensions 和 carDimensions
    // (假设这些是引用类型，如果它们引用的是同一个对象，上面更新 dimensions 时可能已经同步了。
    // 如果它们是独立的对象数组，则需要显式更新)

    // 更新 Gateway 原始列表
    gatewayDimensions.value.forEach((item) => {
      if (newValueMap.has(item.identifier)) {
        item.value = newValueMap.get(item.identifier)
      }
    })

    // 更新 Car 原始列表
    carDimensions.value.forEach((item) => {
      if (newValueMap.has(item.identifier)) {
        item.value = newValueMap.get(item.identifier)
      }
    })
  } catch (error) {
    console.error('Failed to update dimension values:', error)
  }
}

const selectedDate = ref<string[]>([
  ...rangeShortcuts[2].value().map((v) => dayjs(v).format('YYYY-MM-DD HH:mm:ss'))
])

interface ChartData {
  [key: Dimensions['name']]: { ts: number; value: number }[]
}

const chartData = ref<ChartData>({})

let intervalArr = ref<number[]>([])
let maxInterval = ref(0)
let minInterval = ref(0)

const chartRef = ref<HTMLDivElement | null>(null)
let chart: echarts.ECharts | null = null

// const genderIntervalArrDebounce = useDebounceFn(
//   (init: boolean = false) => genderIntervalArr(init),
//   300
// )

function genderIntervalArr(init: boolean = false) {
  const values: number[] = []

  for (const [key, value] of Object.entries(selectedDimension.value)) {
    if (value) {
      values.push(...(chartData.value[key]?.map((item) => item.value) ?? []))
    }
  }

  const maxVal = values.length === 0 ? 10000 : Math.max(...values)
  const minVal = values.length === 0 ? 0 : Math.min(...values) > 0 ? 0 : Math.min(...values)

  const maxDigits = (Math.floor(maxVal) + '').length
  const minDigits = minVal === 0 ? 0 : (Math.floor(Math.abs(minVal)) + '').length

  const interval = Math.max(maxDigits, minDigits)

  maxInterval.value = interval
  minInterval.value = minDigits

  intervalArr.value = [0]
  for (let i = 1; i <= interval; i++) {
    intervalArr.value.push(Math.pow(10, i))
  }

  if (!init) {
    chart?.setOption({
      yAxis: {
        min: -minInterval.value,
        max: maxInterval.value
      }
    })
  }
}

function chartInit() {
  if (!chart) return

  chart.on('legendselectchanged', (params: any) => {
    selectedDimension.value = params.selected
  })

  window.addEventListener('resize', () => {
    if (chart) chart.resize()
  })
}

function render() {
  if (!chartRef.value) return

  if (!chart) chart = echarts.init(chartRef.value, undefined, { renderer: 'canvas' })

  chartInit()

  genderIntervalArr(true)

  chart.setOption({
    grid: {
      left: '6%',
      top: '5%',
      right: '6%',
      bottom: '12%'
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'line'
      },
      formatter: (params) => {
        let d = `${params[0].axisValueLabel}<br>`
        const exist: string[] = []
        params = params.filter((el) => {
          if (exist.includes(el.seriesName)) return false
          exist.push(el.seriesName)
          return true
        })
        let item = params.map(
          (el) => `<div class="flex items-center justify-between mt-1">
            <span>${el.marker} ${el.seriesName}</span>
            <span>${el.value[2]?.toFixed(2)}</span>
          </div>`
        )

        return d + item.join('')
      }
    },
    xAxis: {
      type: 'time',
      axisLabel: {
        formatter: (v) => dayjs(v).format('YYYY-MM-DD\nHH:mm:ss'),
        rotate: 0,
        align: 'left'
      }
    },
    dataZoom: [
      { type: 'inside', xAxisIndex: 0 },
      { type: 'slider', xAxisIndex: 0 }
    ],
    yAxis: {
      type: 'value',
      min: -minInterval.value,
      max: maxInterval.value,
      interval: 1,
      axisLabel: {
        formatter: (v) => {
          const num = v === 0 ? 0 : v > 0 ? Math.pow(10, v) : -Math.pow(10, -v)

          return num.toLocaleString()
        }
      },
      show: false
    },
    legend: {
      data: dimensions.value.map((item) => item.name),
      selected: selectedDimension.value,
      show: false
    },
    series: dimensions.value.map((item) => ({
      name: item.name,
      type: 'line',
      smooth: true,
      showSymbol: false,
      color: item.color,
      data: [] // 占位数组
    }))
  })
}

function mapData({ value, ts }) {
  if (!value) return [ts, 0, 0]

  const isPositive = value > 0
  const absItem = Math.abs(value)

  const min_value = Math.max(...intervalArr.value.filter((v) => v <= absItem))
  const min_index = intervalArr.value.findIndex((v) => v === min_value)

  const new_value =
    (absItem - min_value) / (intervalArr.value[min_index + 1] - intervalArr.value[min_index]) +
    min_index

  return [ts, isPositive ? new_value : -new_value, value]
}

function updateSingleSeries(name: string) {
  if (!chart) render()
  if (!chart) return

  const idx = dimensions.value.findIndex((item) => item.name === name)
  if (idx === -1) return

  const data = chartData.value[name].map((v) => mapData(v))

  chart.setOption({
    series: [{ name, data }]
  })
}

const lastTsMap = ref<Record<Dimensions['name'], number>>({})

async function fetchIncrementData() {
  for (const item of dimensions.value) {
    const { identifier, name } = item

    const lastTs = lastTsMap.value[name]
    if (!lastTs) continue

    item.response = true

    IotStatApi.getDeviceInfoChart(
      data.value.deviceCode,
      identifier,
      dayjs(lastTs).format('YYYY-MM-DD HH:mm:ss'),
      dayjs().format('YYYY-MM-DD HH:mm:ss')
    )
      .then((res) => {
        if (!res.length) return

        const sorted = res
          .sort((a, b) => a.ts - b.ts)
          .map((item) => ({ ts: item.ts, value: item.value }))
        // push 到本地
        chartData.value[name].push(...sorted)
        // 更新 lastTs
        lastTsMap.value[identifier] = sorted.at(-1).ts

        // 更新图表
        updateSingleSeries(name)
      })
      .finally(() => {
        item.response = false
      })
  }
}

const timer = ref<NodeJS.Timeout | null>(null)

function startAutoFetch() {
  timer.value = setInterval(() => {
    updateDimensionValues()
    fetchIncrementData()
  }, 10000)
}

function stopAutoFetch() {
  cancelAllRequests()
  if (timer.value) clearInterval(timer.value)
  timer.value = null
}

const chartLoading = ref(false)

async function initLoadChartData(real_time: boolean = true) {
  if (!dimensions.value.length) return

  chartData.value = Object.fromEntries(dimensions.value.map((item) => [item.name, []]))

  chartLoading.value = true

  dimensions.value = dimensions.value.map((item) => {
    item.response = true
    return item
  })

  for (const item of dimensions.value) {
    const { identifier, name } = item
    try {
      const res = await IotStatApi.getDeviceInfoChart(
        data.value.deviceCode,
        identifier,
        selectedDate.value[0],
        selectedDate.value[1]
      )

      const sorted = res
        .sort((a, b) => a.ts - b.ts)
        .map((item) => ({ ts: item.ts, value: item.value }))

      chartData.value[name] = sorted

      lastTsMap.value[name] = sorted.at(-1)?.ts ?? 0

      updateSingleSeries(name)

      chartLoading.value = false

      if (selectedDimension.value[name]) {
        genderIntervalArr()
      }
    } finally {
      item.response = false
    }
  }

  if (real_time) startAutoFetch()
}

async function initfn(load: boolean = true, real_time: boolean = true) {
  if (load) await loadDimensions()
  render()
  initLoadChartData(real_time)
}

onMounted(() => {
  initfn()
})

function reset() {
  cancelAllRequests().then(() => {
    selectedDate.value = rangeShortcuts[0]
      .value()
      .map((v) => dayjs(v).format('YYYY-MM-DD HH:mm:ss'))

    stopAutoFetch()
    if (chart) chart.clear()
    initfn(false)
  })
}

function handleDateChange() {
  cancelAllRequests().then(() => {
    stopAutoFetch()
    if (chart) chart.clear()
    initfn(false, false)
  })
}

function handleClickSpec(modelName: string) {
  selectedDimension.value[modelName] = !selectedDimension.value[modelName]
  chart?.setOption({
    legend: {
      selected: selectedDimension.value
    }
  })
  chart?.resize()
  genderIntervalArr()
}

const exportChart = () => {
  if (!chart) return
  let img = new Image()
  img.src = chart.getDataURL({
    type: 'png',
    pixelRatio: 1,
    backgroundColor: '#fff'
  })

  img.onload = function () {
    let canvas = document.createElement('canvas')
    canvas.width = img.width
    canvas.height = img.height
    let ctx = canvas.getContext('2d')
    ctx?.drawImage(img, 0, 0)
    let dataURL = canvas.toDataURL('image/png')

    let a = document.createElement('a')

    let event = new MouseEvent('click')

    a.href = dataURL
    a.download = `${data.value.deviceName}-设备监控-${dayjs().format('YYYY-MM-DD HH:mm:ss')}.png`
    a.dispatchEvent(event)
  }
}

const maxmin = computed(() => {
  if (!dimensions.value.length) return []
  return dimensions.value
    .filter((v) => selectedDimension.value[v.name])
    .map((v) => ({
      name: v.name,
      color: v.color,
      max: Math.max(...(chartData.value[v.name]?.map((v) => v.value) ?? [])).toFixed(2),
      min: Math.min(...(chartData.value[v.name]?.map((v) => v.value) ?? [])).toFixed(2)
    }))
})

onUnmounted(() => {
  stopAutoFetch()

  window.removeEventListener('resize', () => {
    if (chart) chart.resize()
  })
})
</script>

<template>
  <div
    class="w-full bg-gradient-to-r from-blue-100 to-white rounded-lg p-6 shadow"
    id="td-device-info"
  >
    <h2 class="flex items-center gap-2">
      <el-icon class="text-sky!"><Odometer /></el-icon> 设备基础信息
    </h2>
    <el-form size="large" label-position="top" class="mt-4 grid grid-cols-3 gap-4">
      <el-form-item label="资产编码"> {{ data.deviceCode }} </el-form-item>
      <el-form-item label="设备类别"> {{ data.deviceName }} </el-form-item>
      <el-form-item label="所在部门"> {{ data.dept }} </el-form-item>
      <el-form-item label="网关状态" class="online" type="plain">
        <el-tag
          v-if="data.ifInline === '3'"
          type="success"
          size="default"
          class="flex items-center"
        >
          <el-icon class="text-emerald!"><CircleCheckFilled /></el-icon>
          在线
        </el-tag>

        <el-tag v-if="data.ifInline === '4'" type="danger" size="default" class="flex items-center">
          <el-icon class="text-rose"><CircleCloseFilled /></el-icon>
          离线
        </el-tag>
      </el-form-item>
      <el-form-item v-if="data.carOnline" label="中航北斗" class="online" type="plain">
        <el-tag
          v-if="data.carOnline === 'true'"
          type="success"
          size="default"
          class="flex items-center"
        >
          <el-icon class="text-emerald!"><CircleCheckFilled /></el-icon>
          在线
        </el-tag>

        <el-tag
          v-if="data.carOnline === 'false'"
          type="danger"
          size="default"
          class="flex items-center"
        >
          <el-icon class="text-rose"><CircleCloseFilled /></el-icon>
          离线
        </el-tag>
      </el-form-item>
      <el-form-item label="最后数据时间"> {{ data.lastInlineTime }} </el-form-item>
      <el-form-item v-if="data.vehicle" label="车牌号码"> {{ data.vehicle }} </el-form-item>
    </el-form>
  </div>
  <div class="mt-4 w-full rounded-lg bg-gradient-to-r from-blue-100 to-white p-4 shadow min-h-50">
    <header class="font-medium text-center w-full">网关数采</header>
    <div
      v-loading="dimensionLoading"
      element-loading-background="transparent"
      class="w-full mt-4 grid grid-cols-4 gap-4 min-h-30"
      id="dimension"
    >
      <button
        v-for="item in gatewayDimensions"
        :key="item.identifier"
        class="border-none h-12 bg-white dark:bg-[#1d1e1f] rounded-lg px-8 shadow flex items-center hover:scale-103 transition-all cursor-pointer"
        :class="{ 'bg-blue-200': selectedDimension[item.name] }"
        :disabled="disabledDimension(item.identifier).disabled"
        @click="handleClickSpec(item.name)"
      >
        <span class="text-sm text-[var(--el-text-color-regular)] flex items-center gap-2 relative">
          <i
            v-show="disabledDimension(item.identifier).loading"
            class="i-line-md:loading-loop size-5 absolute -left-6"
          ></i>
          {{ item.name }}
        </span>
        <span class="text-lg font-medium ms-a">{{ item.value }}</span>
      </button>
    </div>
  </div>
  <div
    v-if="carDimensions.length"
    class="mt-4 w-full rounded-lg bg-gradient-to-r from-blue-100 to-white p-4 shadow"
  >
    <header class="font-medium text-center w-full">中航北斗</header>
    <div class="w-full mt-4 grid grid-cols-4 gap-4" id="dimension">
      <button
        v-for="item in carDimensions"
        :key="item.identifier"
        class="border-none h-12 bg-white dark:bg-[#1d1e1f] rounded-lg px-6 shadow flex items-center hover:scale-103 transition-all cursor-pointer"
        :class="{ 'bg-blue-200': selectedDimension[item.name] }"
        :disabled="disabledDimension(item.identifier).disabled"
        @click="handleClickSpec(item.name)"
      >
        <span class="text-sm text-[var(--el-text-color-regular)] flex items-center gap-2">
          <i
            v-show="disabledDimension(item.identifier).loading"
            class="i-line-md:loading-loop size-5"
          ></i>
          {{ item.name }}
        </span>
        <span class="text-lg font-medium ms-a">{{ item.value }}</span>
      </button>
    </div>
  </div>
  <div class="w-full mt-4 bg-gradient-to-r from-blue-100 to-white min-h-175 rounded-lg p-6 shadow">
    <header class="flex items-center justify-between">
      <h3 class="flex items-center gap-2">
        <div class="i-material-symbols:area-chart-outline-rounded text-sky size-6" text-sky></div>
        数据趋势
      </h3>
      <div class="flex gap-4">
        <el-button type="primary" size="default" @click="exportChart">导出为图片</el-button>
        <el-button size="default" @click="reset">重置</el-button>
        <el-date-picker
          v-model="selectedDate"
          value-format="YYYY-MM-DD HH:mm:ss"
          type="datetimerange"
          unlink-panels
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          :shortcuts="rangeShortcuts"
          size="default"
          class="w-100!"
          placement="bottom-end"
          @change="handleDateChange"
        />
      </div>
    </header>
    <div class="flex h-160 mt-4">
      <div class="flex gap-1">
        <button
          v-for="item of maxmin"
          :key="item.name"
          class="w-8 h-full flex flex-col items-center justify-between py-2 gap-1 rounded bg-transparent border-none"
          @click="handleClickSpec(item.name)"
        >
          <span class="[writing-mode:sideways-lr]">{{ item.max }}</span>
          <div class="flex-1 w-1" :style="{ backgroundColor: item.color }"></div>
          <span class="[writing-mode:sideways-lr]">{{ item.name }}</span>
          <div class="flex-1 w-1" :style="{ backgroundColor: item.color }"></div>
          <span class="[writing-mode:sideways-lr]">{{ item.min }}</span>
        </button>
      </div>
      <div class="flex flex-1">
        <div
          v-loading="chartLoading"
          element-loading-background="transparent"
          ref="chartRef"
          class="flex-1 h-full"
        >
        </div>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
:deep(.el-form-item) {
  margin-bottom: 0;

  .el-form-item__label {
    margin-bottom: 0;
  }

  .el-form-item__content {
    font-size: 1rem;
    font-weight: 500;
  }

  &.online {
    .el-form-item__content {
      height: 2.5rem;

      .el-tag__content {
        display: flex;
        align-items: center;
        gap: 2px;
      }
    }
  }
}
</style>
