﻿<template>
  <el-card class="dip-chart-card" shadow="never">
    <template #header>
      <div class="card-header">
        <div class="card-header-main">
          <div class="card-title">耐受图</div>
          <div class="card-subtitle">
            {{ selectedMappingText }}
          </div>
        </div>

        <el-button
            type="primary"
            plain
            :loading="curveLoading"
            class="draw-curve-button"
            @click="drawCharacteristicCurve"
        >
          绘制特性曲线
        </el-button>
      </div>
    </template>

    <div class="chart-wrapper">
      <MyEchart :options="chartOptions"/>
    </div>

  </el-card>
</template>

<script lang="ts" setup>
import {computed, ref, watch} from 'vue'
import {ElMessage} from 'element-plus'
import MyEchart from '@/components/echarts/line/index.vue'
import {getFreqConverterSCurve} from '@/api/device/freqConverter'

type ChartPointStatus = 'pass' | 'fail'

interface ChartPoint {
  duration: number;
  residualVoltage: number;
  status: ChartPointStatus;
}

interface NormalizedTolerantPoint {
  duration: number;
  residualVoltage: number;
  tolerant: number | null;
  status: ChartPointStatus;
}

const props = defineProps<{
  selectedMapping?: Record<string, any> | null;
  webMsgSend?: any;
  resultData?: any;
}>()

const STATUS_COLOR_MAP: Record<ChartPointStatus, string> = {
  pass: '#1d4ed8',
  fail: '#111111'
}

const chartPoints = ref<ChartPoint[]>([])
const characteristicCurveData = ref<Array<[number, number]>>([])
const curveLoading = ref(false)

const selectedMappingText = computed(() => {
  if (!props.selectedMapping) {
    return '未选择变频器'
  }

  return `变频器：${props.selectedMapping.freqConverterName || '-'}`
})

const normalizeTolerantValue = (value: unknown) => {
  if (value === undefined || value === null || value === '') {
    return null
  }

  const result = Number(value)
  if ([0, 1, 2].includes(result)) {
    return result
  }

  return null
}

const normalizeDuration = (source: Record<string, any>) => {
  return toNumber(
    source.durationMs !== undefined && source.durationMs !== null
      ? Number(source.durationMs) / 1000
      : source.duration ??
        source.x ??
        source.dipDuration ??
        source.retainTime ??
        source.durationValue
  )
}

const normalizeResidualVoltageValue = (source: Record<string, any>) => {
  return toNumber(
    source.residualVoltage ??
    source.y ??
    source.residual ??
    source.voltage ??
    source.residual_value
  )
}

const normalizeCurveData = (source: unknown) => {
  if (!Array.isArray(source)) {
    return [] as Array<[number, number]>
  }

  return source
      .map(item => {
        if (Array.isArray(item) && item.length >= 2) {
          const duration = Number(item[0])
          const residualVoltage = Number(item[1])
          if (Number.isFinite(duration) && Number.isFinite(residualVoltage)) {
            return [duration, residualVoltage] as [number, number]
          }
        }

        if (item && typeof item === 'object') {
          const record = item as Record<string, any>
          const tolerant = normalizeTolerantValue(record.tolerant)
          if (tolerant !== null && tolerant !== 2) {
            return null
          }

          const duration = normalizeDuration(record)
          const residualVoltage = normalizeResidualVoltageValue(record)
          if (duration !== null && residualVoltage !== null) {
            return [duration, residualVoltage] as [number, number]
          }
        }

        return null
      })
      .filter((item): item is [number, number] => !!item)
}

const extractCurveData = (payload: any) => {
  if (!payload) {
    return [] as Array<[number, number]>
  }

  const candidates = [
    payload,
    payload?.data,
    payload?.data?.records,
    payload?.data?.points,
    payload?.points,
    payload?.records,
    payload?.list
  ]

  for (const candidate of candidates) {
    const normalized = normalizeCurveData(candidate)
    if (normalized.length) {
      return normalized
    }
  }

  return [] as Array<[number, number]>
}

const drawCharacteristicCurve = async () => {
  const freqConverterId = props.selectedMapping?.freqConverterId
  if (!freqConverterId) {
    ElMessage.warning('未获取到变频器ID')
    return
  }

  curveLoading.value = true

  try {
    const result = await getFreqConverterSCurve({
      converterId: freqConverterId
    })

    const normalizedCurveData = extractCurveData(result)

    if (!normalizedCurveData.length) {
      characteristicCurveData.value = []
      ElMessage.warning('未获取到特性曲线数据')
      return
    }

    characteristicCurveData.value = normalizedCurveData
  } catch (error) {
    console.error('绘制特性曲线失败:', error)
    characteristicCurveData.value = []
  } finally {
    curveLoading.value = false
  }
}

const chartOptions = computed(() => {
  const maxDuration = 2
  // const maxDuration = Math.max(
  //   2,
  //   ...chartPoints.value.map(item => item.duration).filter(item => Number.isFinite(item)),
  //   ...characteristicCurveData.value.map(item => item[0]).filter(item => Number.isFinite(item))
  // )

  return {
    title: {
      text: ''
    },
    grid: {
      top: 30,
      left: 48,
      right: 22,
      bottom: 52
    },
    tooltip: {
      trigger: 'item',
      formatter(params: any) {
        if (params.seriesType === 'line') {
          return ''
        }

        const [duration, residualVoltage, statusText] = params.value
        return [
          `持续时间: ${duration} s`,
          `残余电压: ${residualVoltage} %`,
          `状态: ${statusText}`
        ].join('<br/>')
      }
    },
    legend: {
      top: 0,
      right: 0,
      data: ['特性测试曲线']
    },
    xAxis: {
      type: 'log',
      name: '持续时间(s)',
      nameLocation: 'middle',
      nameGap: 34,
      min: 0.01,
      max: 60,
      logBase: 10,
      minorTick: {
        show: true,
        splitNumber: 10
      },
      minorSplitLine: {
        show: true,
        lineStyle: {
          color: '#e8edf6'
        }
      },
      splitLine: {
        lineStyle: {
          color: '#cfd8e6'
        }
      },
      axisLabel: {
        formatter(value: number) {
          return value.toFixed(2)
        }
      }
    },
    yAxis: {
      type: 'value',
      name: '暂降幅值',
      min: 0,
      max: 100,
      interval: 10,
      minorTick: {
        show: true,
        splitNumber: 2
      },
      minorSplitLine: {
        show: true,
        lineStyle: {
          color: '#e8edf6'
        }
      },
      splitLine: {
        lineStyle: {
          color: '#cfd8e6'
        }
      },
      axisLabel: {
        formatter(value: number) {
          return `${value}%`
        }
      }
    },
    dataZoom: [],
    series: [
      {
        name: '特性测试曲线',
        type: 'line',
        smooth: true,
        showSymbol: true,
        symbolSize: 7,
        lineStyle: {
          color: '#ff2a2a',
          width: 3
        },
        itemStyle: {
          color: '#ff2a2a'
        },
        data: characteristicCurveData.value
      },
      {
        name: '暂降点',
        type: 'scatter',
        symbolSize: 10,
        data: chartPoints.value.map(item => ({
          value: [item.duration, item.residualVoltage, getStatusText(item.status)],
          itemStyle: {
            color: STATUS_COLOR_MAP[item.status]
          }
        }))
      }
    ],
    options: {
      animation: false
    }
  }
})

const toNumber = (value: unknown) => {
  const result = Number(value)
  return Number.isFinite(result) ? result : null
}

const normalizeStatus = (value: unknown): ChartPointStatus => {
  const rawValue = `${value ?? ''}`.trim().toLowerCase()

  if (
    value === 0 ||
    rawValue === '0' ||
    rawValue === 'false' ||
    rawValue === 'fail' ||
    rawValue === 'failed' ||
    rawValue.includes('不耐受')
  ) {
    return 'fail'
  }

  return 'pass'
}

const normalizeTolerantPoint = (source: Record<string, any>): NormalizedTolerantPoint | null => {
  const duration = normalizeDuration(source)
  const residualVoltage = normalizeResidualVoltageValue(source)

  if (duration === null || residualVoltage === null) {
    return null
  }

  if (duration <= 0 || residualVoltage < 0 || residualVoltage > 100) {
    return null
  }

  const tolerant = normalizeTolerantValue(
    source.tolerant ??
    source.endure ??
    source.isEndure ??
    source.tolerable ??
    source.isTolerable ??
    source.status ??
    source.pointStatus ??
    source.result ??
    source.state
  )

  return {
    duration,
    residualVoltage,
    tolerant,
    status:
      tolerant === 0
        ? 'fail'
        : tolerant === 1
          ? 'pass'
          : normalizeStatus(
            source.tolerant ??
            source.endure ??
            source.isEndure ??
            source.tolerable ??
            source.isTolerable ??
            source.status ??
            source.pointStatus ??
            source.result ??
            source.state
          )
  }
}

const getStatusText = (status: ChartPointStatus) => {
  if (status === 'fail') {
    return '不耐受'
  }

  return '耐受'
}

const normalizePoint = (source: Record<string, any>): ChartPoint | null => {
  const point = normalizeTolerantPoint(source)
  if (!point || point.tolerant === 2) {
    return null
  }

  return {
    duration: point.duration,
    residualVoltage: point.residualVoltage,
    status: point.status
  }
}

const extractCharacteristicCurvePoints = (payload: any) => {
  const result: Array<[number, number]> = []
  const seen = new Set<string>()
  const rootPayload = payload?.data && typeof payload.data === 'object' ? payload.data : payload

  const walk = (node: any) => {
    if (!node) {
      return
    }

    if (Array.isArray(node)) {
      node.forEach(item => walk(item))
      return
    }

    if (typeof node !== 'object') {
      return
    }

    const point = normalizeTolerantPoint(node)
    if (point?.tolerant === 2) {
      const key = `${point.duration}|${point.residualVoltage}`
      if (!seen.has(key)) {
        seen.add(key)
        result.push([point.duration, point.residualVoltage])
      }
    }

    Object.values(node).forEach(item => {
      if (item && typeof item === 'object') {
        walk(item)
      }
    })
  }

  walk(rootPayload)
  return result
}

const extractPoints = (payload: any) => {
  const result: ChartPoint[] = []
  const seen = new Set<string>()
  const rootPayload = payload?.data && typeof payload.data === 'object' ? payload.data : payload

  const walk = (node: any) => {
    if (!node) {
      return
    }

    if (Array.isArray(node)) {
      node.forEach(item => walk(item))
      return
    }

    if (typeof node !== 'object') {
      return
    }

    const point = normalizePoint(node)
    if (point) {
      const key = `${point.duration}|${point.residualVoltage}`
      if (!seen.has(key)) {
        seen.add(key)
        result.push(point)
      }
    }

    Object.values(node).forEach(item => {
      if (item && typeof item === 'object') {
        walk(item)
      }
    })
  }

  walk(rootPayload)
  return result
}

watch(
    () => props.webMsgSend,
    newValue => {
      if (!newValue) {
        return
      }

      const nextPoints = extractPoints(newValue)
      if (!nextPoints.length) {
        const nextCurvePoints = extractCharacteristicCurvePoints(newValue)
        if (nextCurvePoints.length) {
          characteristicCurveData.value = nextCurvePoints
        }
        return
      }

      const existingPointMap = new Map(
        chartPoints.value.map(item => [`${item.duration}|${item.residualVoltage}`, item] as const)
      )

      nextPoints.forEach(item => {
        const key = `${item.duration}|${item.residualVoltage}`
        existingPointMap.set(key, item)
      })

      chartPoints.value = Array.from(existingPointMap.values())

      const nextCurvePoints = extractCharacteristicCurvePoints(newValue)
      if (nextCurvePoints.length) {
        characteristicCurveData.value = nextCurvePoints
      }
    },
    {deep: true}
)

watch(
    () => props.resultData,
    newValue => {
      if (!newValue) {
        return
      }

      chartPoints.value = extractPoints(newValue)
      const nextCurvePoints = extractCharacteristicCurvePoints(newValue)
      if (nextCurvePoints.length) {
        characteristicCurveData.value = nextCurvePoints
      }
    },
    {deep: true, immediate: true}
)

watch(
    () => props.selectedMapping,
    () => {
      chartPoints.value = []
      characteristicCurveData.value = []
    }
)
</script>

<style scoped>
.dip-chart-card {
  border: 1px solid var(--el-border-color-light);
}

:deep(.dip-chart-card .el-card__header) {
  padding: 10px 14px;
}

:deep(.dip-chart-card .el-card__body) {
  padding: 10px 14px 14px;
}

.card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.card-header-main {
  min-width: 0;
}

.card-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}

.card-subtitle {
  margin-top: 4px;
  font-size: 13px;
  color: var(--el-text-color-secondary);
}

.draw-curve-button {
  margin-top: -2px;
  flex-shrink: 0;
}

.chart-wrapper {
  height: 400px;
}
</style>
