<template>
  <BaseCard title="采购商户来源分析">
    <template #headerExtra>
      <span class="unit-label">采购商户数量(家)</span>
    </template>

    <div class="chart-wrapper">
      <div ref="chartRef" class="chart"></div>
      <img
        ref="baseRef"
        class="chart-base"
        src="/images/底座.png"
        alt=""
      />
    </div>
  </BaseCard>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import BaseCard from './BaseCard.vue'
import { bindChartBaseSync, syncChartBaseToGrid } from '../utils/chartBaseLayout.js'

const API_URL = '/api/dashboard/buyer-source-analysis'

const CHART_GRID = {
  left: 48,
  right: 24,
  bottom: 56,
  top: 18
}

const BAR_WIDTH = 13
const CAP_RY = 3.5
const MIN_BAR_PX = 8

const CYLINDER_PALETTES = [
  {
    edge: 'rgba(44, 162, 163, 0.60)',
    center: 'rgba(2, 206, 214, 0.80)',
    capCenter: 'rgba(2, 206, 214, 0.95)',
    capEdge: 'rgba(44, 162, 163, 0.70)',
    bottom: 'rgba(44, 162, 163, 0.42)',
    zeroCenter: 'rgba(2, 206, 214, 0.45)'
  },
  {
    edge: 'rgba(44, 162, 163, 0.60)',
    center: 'rgba(25, 217, 170, 0.80)',
    capCenter: 'rgba(25, 217, 170, 0.95)',
    capEdge: 'rgba(44, 162, 163, 0.70)',
    bottom: 'rgba(44, 162, 163, 0.42)',
    zeroCenter: 'rgba(25, 217, 170, 0.45)'
  }
]

const ZERO_EDGE = 'rgba(44, 162, 163, 0.35)'
const ZERO_BOTTOM = 'rgba(44, 162, 163, 0.22)'

const chartRef = ref(null)
const baseRef = ref(null)
const regionsData = ref([])
let chartInstance = null
let tipTimer = null
let tipIndex = 0
let unbindChartBaseSync = null

const calcAxisMax = (values, ratio = 1.25) => {
  const maxVal = Math.max(0, ...values.map((v) => Number(v) || 0))
  if (maxVal === 0) {
    return 5
  }

  const raw = maxVal * ratio
  const magnitude = Math.pow(10, Math.floor(Math.log10(raw)))
  const normalized = raw / magnitude
  let nice = 10

  if (normalized <= 1) nice = 1
  else if (normalized <= 2) nice = 2
  else if (normalized <= 5) nice = 5

  return nice * magnitude
}

const getZeroDisplayValue = (axisMax) => axisMax * 0.04

const createCylinderBarRenderer = (axisMax) => (params, api) => {
  const categoryIndex = params.dataIndex
  const rawValue = Number(api.value(1)) || 0
  const displayValue = rawValue > 0 ? rawValue : getZeroDisplayValue(axisMax)
  const isZero = rawValue === 0

  const base = api.coord([categoryIndex, 0])
  const top = api.coord([categoryIndex, displayValue])
  const cx = base[0]
  const topY = top[1]
  const bodyTop = topY + CAP_RY
  const bodyHeight = Math.max(base[1] - bodyTop, MIN_BAR_PX)
  const x = cx - BAR_WIDTH / 2
  const palette = CYLINDER_PALETTES[categoryIndex % 2]

  const bodyGradient = new echarts.graphic.LinearGradient(0, 0, 1, 0, [
    { offset: 0, color: isZero ? ZERO_EDGE : palette.edge },
    { offset: 0.5, color: isZero ? palette.zeroCenter : palette.center },
    { offset: 1, color: isZero ? ZERO_EDGE : palette.edge }
  ])

  const capGradient = new echarts.graphic.LinearGradient(0, 0, 1, 0, [
    { offset: 0, color: isZero ? ZERO_EDGE : palette.capEdge },
    { offset: 0.5, color: isZero ? palette.zeroCenter : palette.capCenter },
    { offset: 1, color: isZero ? ZERO_EDGE : palette.capEdge }
  ])

  const bottomCap = {
    type: 'ellipse',
    shape: {
      cx,
      cy: base[1],
      rx: BAR_WIDTH / 2,
      ry: CAP_RY * 0.55
    },
    style: {
      fill: isZero ? ZERO_BOTTOM : palette.bottom
    }
  }

  const body = {
    type: 'rect',
    shape: {
      x,
      y: bodyTop,
      width: BAR_WIDTH,
      height: bodyHeight,
      r: [0, 0, 0, 0]
    },
    style: {
      fill: bodyGradient
    }
  }

  const topCap = {
    type: 'ellipse',
    shape: {
      cx,
      cy: bodyTop,
      rx: BAR_WIDTH / 2,
      ry: CAP_RY
    },
    style: {
      fill: capGradient
    }
  }

  return {
    type: 'group',
    children: [bottomCap, body, topCap]
  }
}

const buildChartOption = (axisMax) => {
  const labels = regionsData.value.map((item) => item.name)
  const values = regionsData.value.map((item) => item.value)

  return {
    animation: true,
    animationDuration: 800,
    animationEasing: 'cubicOut',
    tooltip: {
      trigger: 'axis',
      backgroundColor: 'rgba(6, 28, 58, 0.94)',
      borderColor: '#41a6fc',
      borderWidth: 1,
      padding: [10, 14],
      textStyle: {
        color: '#ffffff',
        fontSize: 13,
        fontFamily: 'Microsoft YaHei, sans-serif'
      },
      extraCssText: 'box-shadow: 0 0 14px rgba(65, 166, 252, 0.35); border-radius: 6px;',
      formatter(params) {
        const idx = params[0]?.dataIndex ?? 0
        const item = regionsData.value[idx]
        if (!item) {
          return ''
        }
        return [
          `<div style="font-weight:700;margin-bottom:6px;">${item.name}</div>`,
          `<div style="color:#41a6fc;margin-top:2px;">采购商户：${item.value}家</div>`,
          `<div style="color:rgba(255,255,255,0.72);margin-top:4px;font-size:12px;">${item.description}</div>`
        ].join('')
      }
    },
    grid: CHART_GRID,
    xAxis: {
      type: 'category',
      data: labels,
      boundaryGap: true,
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: {
        color: '#ffffff',
        fontSize: 11,
        fontFamily: 'Microsoft YaHei, sans-serif',
        interval: 0,
        margin: 10,
        formatter(value) {
          if (value.length <= 5) {
            return value
          }
          return `${value.slice(0, 5)}\n${value.slice(5)}`
        }
      }
    },
    yAxis: {
      type: 'value',
      min: 0,
      max: axisMax,
      splitNumber: 5,
      splitLine: {
        lineStyle: {
          color: 'rgba(80, 160, 220, 0.22)',
          type: 'dashed'
        }
      },
      axisLine: { show: false },
      axisTick: { show: false },
      axisLabel: {
        color: '#ffffff',
        fontSize: 12,
        fontFamily: 'Microsoft YaHei, sans-serif'
      }
    },
    series: [{
      name: '采购商户数量',
      type: 'custom',
      renderItem: createCylinderBarRenderer(axisMax),
      data: values.map((value, index) => [index, value]),
      encode: { x: 0, y: 1 },
      z: 2
    }]
  }
}

const updateChart = () => {
  if (!chartInstance || !regionsData.value.length) {
    return
  }

  const values = regionsData.value.map((item) => item.value)
  const axisMax = calcAxisMax(values, 1.3)
  chartInstance.setOption(buildChartOption(axisMax), true)
  syncChartBaseToGrid(chartInstance, baseRef.value)
}

const startTipCarousel = () => {
  stopTipCarousel()
  if (!chartInstance || regionsData.value.length === 0) {
    return
  }

  tipTimer = setInterval(() => {
    chartInstance.dispatchAction({ type: 'hideTip' })
    chartInstance.dispatchAction({
      type: 'showTip',
      seriesIndex: 0,
      dataIndex: tipIndex
    })
    tipIndex = (tipIndex + 1) % regionsData.value.length
  }, 3000)
}

const stopTipCarousel = () => {
  if (tipTimer) {
    clearInterval(tipTimer)
    tipTimer = null
  }
}

const loadData = async () => {
  try {
    const response = await fetch(API_URL)
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`)
    }

    const result = await response.json()
    if (result.code !== 1 || !result.data) {
      throw new Error(result.message || '接口返回异常')
    }

    regionsData.value = (result.data || []).map((item) => ({
      name: item.name || item.buyerPlace || item.buyer_place || '',
      value: Number(item.value) || 0,
      fullName: item.fullName || item.full_name || item.name || '',
      description: item.description || `${item.name || item.buyerPlace || ''}采购商户`
    }))
    tipIndex = 0
    updateChart()
    startTipCarousel()
  } catch (error) {
    console.error('加载采购商户来源分析失败:', error)
  }
}

const handleResize = () => {
  chartInstance?.resize()
  syncChartBaseToGrid(chartInstance, baseRef.value)
}

onMounted(async () => {
  if (chartRef.value) {
    chartInstance = echarts.init(chartRef.value)
    unbindChartBaseSync = bindChartBaseSync(chartInstance, baseRef.value)
  }
  await loadData()
  window.addEventListener('resize', handleResize)
  window.addEventListener('dataReloaded', loadData)
})

onUnmounted(() => {
  stopTipCarousel()
  window.removeEventListener('resize', handleResize)
  window.removeEventListener('dataReloaded', loadData)
  unbindChartBaseSync?.()
  chartInstance?.dispose()
})
</script>

<style scoped>
.unit-label {
  font-family: 'Microsoft YaHei', sans-serif;
  font-size: 13px;
  color: rgba(255, 255, 255, 0.82);
  white-space: nowrap;
  padding-right: 4px;
}

.chart-wrapper {
  position: relative;
  flex: 1;
  min-height: 0;
  width: 100%;
  height: 100%;
}

.chart {
  width: 100%;
  height: 100%;
  min-height: 180px;
}

.chart-base {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: auto;
  pointer-events: none;
  user-select: none;
  z-index: 0;
}
</style>
