<template>
  <div ref="differenceChartRef" class="echart_content"></div>
</template>

<script setup lang="ts">
import { ref, watch, onMounted, onBeforeUnmount, nextTick, withDefaults } from 'vue'
import _ from 'lodash'
import * as echarts from 'echarts'
import { getFormatNumber } from '@/utils/common'

// ===================== 类型定义 =====================
/** 悬浮提示额外数据项 */
interface TooltipListItem {
  name: string
  value: string | number
}

/** 单条悬浮数据 */
interface TooltipDataItem {
  list: TooltipListItem[]
}

/** Props 类型 */
interface DifferenceChartProps {
  datax: string[]
  datay: (string | number)[]
  tooltipData?: TooltipDataItem[]
  title: string
  rate: number | string
  unit: string
  type: string
  isClick?: boolean
  color?: string[]
  gridLeft?: number
  gridRight?: number
  gridTop?: number
  fontSize?: number | string
  fontColor?: string
  showDataZoom?: boolean
}

/** y轴最大最小值返回结构 */
interface MaxMinResult {
  maxValue: number
  minValue: number
}

// ===================== 常量配置 =====================
const BAR_MIN_WIDTH = 30
const THROTTLE_DELAY = 500
const DPR = 2
const HIGHLIGHT_OPACITY = '30'
const DEFAULT_GRID_HORIZONTAL = 140

// ===================== Props 配置（TS + withDefaults） =====================
const props = withDefaults(defineProps<DifferenceChartProps>(), {
  datax: () => [],
  datay: () => [],
  tooltipData: () => [],
  title: '率差',
  rate: 60,
  unit: '',
  type: '0',
  isClick: true,
  color: () => ['#3BA272', '#EE6666'],
  gridLeft: 40,
  gridRight: 60,
  gridTop: 20,
  fontSize: '',
  fontColor: '',
  showDataZoom: true
})

// 事件派发
const emit = defineEmits<{
  HandleChartClick: [index: number, name: string]
}>()

// ===================== 响应式变量 =====================
const differenceChartRef = ref<HTMLDivElement | null>(null)
let echart: echarts.ECharts | null = null

// 窗口节流监听
const handleResize = _.throttle(async () => {
  await nextTick()
  loadEchart()
}, THROTTLE_DELAY)

// 组件初始化阶段直接绑定 resize 监听（替代 onCreated）
window.addEventListener('resize', handleResize)

// ===================== 工具函数 =====================
/**
 * 计算Y轴最大/最小值
 */
function getMaxMinValue(max: number, min: number, rateValue: number): MaxMinResult {
  let maxValue: number
  let minValue: number

  if (rateValue === 0) {
    const differenceValue = Math.abs(max) > Math.abs(min) ? Math.abs(max) : Math.abs(min)
    const thresholdMap = [
      { maxThreshold: 0.3, bound: 0.3 },
      { maxThreshold: 0.6, bound: 0.6 },
      { maxThreshold: 1, bound: 1 },
      { maxThreshold: 1.5, bound: 1.5 },
      { maxThreshold: 2, bound: 2 },
      { maxThreshold: 4, bound: 4 },
      { maxThreshold: 6, bound: 10 }
    ]
    const matchedRule = thresholdMap.find(item => differenceValue < item.maxThreshold)
    const bound = matchedRule ? matchedRule.bound : getCustomRoundUp(differenceValue)

    maxValue = bound
    minValue = -bound
  } else {
    const maxValues = max - rateValue
    const minValues = min - rateValue
    const differenceValue = Math.abs(maxValues) > Math.abs(minValues) ? Math.abs(maxValues) : Math.abs(minValues)
    const val = getCustomRoundUp(differenceValue)
    maxValue = val
    minValue = -val
  }

  return { maxValue, minValue }
}

/**
 * 数值向上取整到10的倍数
 */
function getCustomRoundUp(number: number): number {
  const nearest10 = Math.ceil(number / 10) * 10
  return nearest10
}

/**
 * 生成 DataZoom 滚动条配置
 */
function getDataZoom(totalWidth: number, xLen: number): echarts.DataZoomSliderOption {
  const dataZoomNum = Math.floor((totalWidth - DEFAULT_GRID_HORIZONTAL) / BAR_MIN_WIDTH)
  const dataZoomEnd = Math.floor((100 / xLen) * dataZoomNum)

  return {
    start: 0,
    end: dataZoomEnd,
    type: 'slider',
    show: true,
    borderColor: 'transparent',
    borderCap: 'round',
    xAxisIndex: [0],
    height: 8,
    left: 20,
    right: 20,
    bottom: 0,
    fillerColor: 'transparent',
    zoomLock: true,
    handleSize: '0',
    handleStyle: {
      color: '#b8b8b8',
      borderWidth: 2
    },
    backgroundColor: 'transparent',
    showDataShadow: false,
    showDetail: false,
    filterMode: 'filter'
  }
}

/**
 * X轴文字超长截断
 */
function formatXLabel(value: string, singleSeriesWidth: number): string {
  const charUnit = 14
  const valueWidth = value.length * charUnit

  if (valueWidth <= singleSeriesWidth) return value

  if (singleSeriesWidth < 80) {
    return valueWidth > 80 ? `${value.slice(0, 2)}...${value.slice(-3)}` : value
  }

  const maxLength = Math.floor(singleSeriesWidth / charUnit) - 1
  return value.slice(0, maxLength) + '...'
}

/**
 * 绑定柱子点击 & 高亮样式
 */
function bindBarClick(totalWidth: number, singleLabelWidth: number, gridRect: echarts.Rect) {
  if (!echart) return
  const xData = props.datax

  // 初始高亮第一个
  if (xData.length > 0) {
    const defaultPixelPos = echart.convertToPixel({ xAxisIndex: 0 }, xData[0])
    echart.setOption({
      graphic: {
        id: 'highlight-box',
        type: 'rect',
        shape: {
          x: defaultPixelPos - singleLabelWidth / 2,
          y: gridRect.y,
          width: singleLabelWidth,
          height: gridRect.height
        },
        style: {
          fill: 'rgba(84,112,198,0.1)'
        }
      }
    })
  }

  // 点击事件
  echart.on('click', (params) => {
    if (params.seriesType !== 'bar') return

    // 修改x轴文字颜色
    echart.setOption({
      xAxis: [{
        axisLabel: {
          color: (_val: string, index: number) => {
            return index === params.dataIndex ? '#2E64FA' : '#333'
          }
        }
      }]
    })

    // 更新高亮背景
    const pixelPosition = echart.convertToPixel({ xAxisIndex: 0 }, params.name)
    echart.setOption({
      graphic: {
        id: 'highlight-box',
        type: 'rect',
        shape: {
          x: pixelPosition - singleLabelWidth / 2,
          y: gridRect.y,
          width: singleLabelWidth,
          height: gridRect.height
        },
        style: {
          fill: (params.color as string) + HIGHLIGHT_OPACITY
        }
      }
    })

    emit('HandleChartClick', params.dataIndex, params.name as string)
  })
}

// ===================== 核心：初始化图表 =====================
function loadEchart() {
  const dom = differenceChartRef.value
  if (!dom) return

  // 销毁旧实例，防止内存泄漏
  if (echart) {
    echart.dispose()
    echart = null
  }

  echart = echarts.init(dom, null, { devicePixelRatio: DPR })
  const { datax, datay, unit, type, rate, color, tooltipData, showDataZoom } = props
  const xLen = datax.length
  if (xLen === 0) return

  // 计算单柱宽度
  const totalWidth = dom.clientWidth
  const singleSeriesWidth = Math.ceil((totalWidth - DEFAULT_GRID_HORIZONTAL) / xLen)

  // 基准线数值
  const rateValue = Number(rate) || 0

  // 处理y轴原始数据
  const numArr = datay.map(item => Number(item))
  const max = Math.max(...numArr)
  const min = Math.min(...numArr)
  const { maxValue, minValue } = getMaxMinValue(max, min, rateValue)

  // 滚动条配置
  const dataZoomOpt = showDataZoom && singleSeriesWidth < BAR_MIN_WIDTH
    ? getDataZoom(totalWidth, xLen)
    : null

  // 处理柱子数据（颜色、标签）
  const processedData = datay.map((yVal) => {
    const numVal = Number(yVal) - rateValue
    const fixedVal = Number(numVal.toFixed(2))

    return {
      value: fixedVal,
      label: {
        show: singleSeriesWidth > 22,
        position: fixedVal > 0 ? 'top' : 'bottom',
        formatter: (params: echarts.LabelFormatterParams) => {
          if (type === '1') {
            return `${getFormatNumber(Number(params.value).toFixed(2))}${unit}`
          } else {
            const originVal = (Number(params.value) + rateValue).toFixed(2)
            return `${getFormatNumber(originVal)}${unit}`
          }
        },
        color: props.fontColor || '#666',
        fontSize: props.fontSize ? Number(props.fontSize) : 14
      },
      itemStyle: {
        color: fixedVal > 0 ? color[0] : color[1]
      }
    }
  })

  // ECharts 配置项
  const option: echarts.EChartsOption = {
    tooltip: {
      trigger: 'item',
      triggerOn: 'mousemove',
      confine: true,
      borderColor: '#fff',
      extraCssText: 'border-radius: 4px;padding:5px 0 5px 5px;white-space:normal;word-wrap:break-word;max-width: 400px;',
      formatter: (params) => {
        const p = Array.isArray(params) ? params[0] : params
        const title = p.name ?? ''
        const tipType = type === '1' ? '分差' : props.title
        let showVal = ''

        if (type === '1') {
          showVal = `${p.value}${unit}`
        } else {
          const originVal = (Number(p.value) + rateValue).toFixed(2)
          showVal = `${originVal}${unit}`
        }

        let tooltip = `<div class="tooltip_content"><div class="tooltip_title">${title}</div>`
        const tipItem = tooltipData[p.dataIndex]

        if (tipItem?.list?.length) {
          tipItem.list.forEach(item => {
            tooltip += `<div class="tooltip_student">${item.name}：${item.value}</div>`
          })
        } else {
          tooltip += `<div class="tooltip_student">${tipType}：${showVal}</div>`
        }
        tooltip += '</div>'
        return tooltip
      }
    },
    grid: {
      left: props.gridLeft,
      right: props.gridRight,
      top: props.gridTop,
      bottom: 0,
      containLabel: true
    },
    dataZoom: dataZoomOpt,
    xAxis: [{
      type: 'category',
      data: datax,
      axisPointer: {
        type: 'shadow'
      },
      axisLabel: {
        interval: 0,
        margin: 20,
        rotate: singleSeriesWidth < 80 ? 45 : 0,
        fontSize: props.fontSize ? Number(props.fontSize) : 14,
        color: props.fontColor || '#666',
        fontWeight: 400,
        formatter: (val) => formatXLabel(val, singleSeriesWidth)
      }
    }],
    yAxis: {
      type: 'value',
      max: maxValue,
      min: minValue,
      axisLabel: {
        formatter: (value) => {
          const totalVal = Number(value) + rateValue
          return `${getFormatNumber(totalVal)}${unit}`
        },
        color: props.fontColor || '#666',
        fontSize: props.fontSize ? Number(props.fontSize) : 14
      },
      splitLine: {
        show: true,
        lineStyle: {
          color: '#E4E7ED',
          type: 'solid'
        }
      },
      axisLine: {
        show: false,
        lineStyle: {
          color: '#E4E7ED',
          width: 1
        }
      },
      splitArea: {
        show: true,
        areaStyle: {
          color: ['#fafafa', '#ffffff']
        }
      }
    },
    series: [{
      type: 'bar',
      emphasis: {
        focus: 'series'
      },
      barMinHeight: 1,
      barMaxWidth: 50,
      barMinWidth: showDataZoom ? 20 : 1,
      data: processedData
    }]
  }

  echart.setOption(option)

  // 开启点击与高亮
  if (props.isClick) {
    const gridModel = echart.getModel().getComponent('grid')
    const gridRect = gridModel.coordinateSystem.getRect()
    const singleLabelWidth = gridRect.width / xLen
    bindBarClick(totalWidth, singleLabelWidth, gridRect)
  }
}

// ===================== 监听 & 生命周期 =====================
watch(
  () => props.datay,
  () => loadEchart(),
  { deep: true }
)

onMounted(() => {
  window.addEventListener("resize", handleResize); //创建时增加监听窗口变化事件
  loadEchart()
})

// 组件卸载前：移除监听 + 销毁图表实例
onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
  if (echart) {
    echart.dispose()
    echart = null
  }
})
</script>

<style lang="scss" scoped>
.echart_content {
  position: relative;
  width: 100%;
  margin: auto;
  min-height: 360px;
  height: auto;
}

//弹窗悬浮层样式更改
.echart_content::-webkit-scrollbar {
  width: 100%;
  height: 8px;
  /* 设置滚动条宽度为8像素 */
  background-color: transparent;
}

/* 滑块样式 */
.echart_content::-webkit-scrollbar-thumb {
  background-color: #b8b8b8;
  /* 设置滑块颜色为深灰色 */
  border-radius: 4px;
  /* 设置滑块边角半径为4像素 */
  min-height: 40px; //设置手柄最小高度
}

/* 滚动条轨道内部空白区域样式 */
.echart_content::-webkit-scrollbar-track {
  background-color: #f0f0f0;
  /* 设置轨道背景色为浅灰色 */
}

/* 滚动条两端按钮样式 */
.echart_content::-webkit-scrollbar-button {
  display: none;
  /* 不显示按钮 */
}

/* 交叉点处的区域样式 */
.echart_content::-webkit-scrollbar-corner {
  background-color: transparent;
  /* 设置交叉点处的背景色为透明 */
}

/* 调整大小手柄样式 */
.echart_content::-webkit-resizer {
  display: none;
  /* 不显示调整大小手柄 */
}
</style>