<template>
  <BaseCard title="交易所牦牛成交数据">
    <template #headerExtra>
      <DateIndicatorTabs
        v-model="selectedPeriod"
        :items="timePeriods"
      />
    </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, watch } from 'vue'
import * as echarts from 'echarts'
import BaseCard from './BaseCard.vue'
import DateIndicatorTabs from './DateIndicatorTabs.vue'
import { bindChartBaseSync, syncChartBaseToGrid } from '../utils/chartBaseLayout.js'

const chartRef = ref(null)
const baseRef = ref(null)
let chartInstance = null
let growAnimationFrame = null
let unbindChartBaseSync = null

const CHART_GRID_BOTTOM = 30
const CHART_GRID_BOTTOM_WEEK = 44
const GROW_ANIMATION_MS = 900

const easeOutCubic = (t) => 1 - (1 - t) ** 3

const selectedPeriod = ref('day')
const timePeriods = [
  { key: 'day', label: '日' },
  { key: 'week', label: '周' },
  { key: 'month', label: '月' }
]

const chartData = ref({
  day: { labels: [], yakNumber: [], orderCount: [] },
  week: { labels: [], yakNumber: [], orderCount: [] },
  month: { labels: [], yakNumber: [], orderCount: [] }
})

const API_URL = '/api/dashboard/yak-trading-data'

const normalizePeriodData = (data = {}) => ({
  labels: Array.isArray(data.labels) ? data.labels : [],
  yakNumber: (data.yakNumber ?? data.yak_number ?? []).map((v) => Number(v) || 0),
  orderCount: (data.orderCount ?? data.order_count ?? []).map((v) => Number(v) || 0)
})

const BAR_WIDTH = 20
const BAR_DEPTH_X = 7
const BAR_DEPTH_Y = 5
const MIN_BAR_PX = 6

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

  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
}

// 0 值在轴上映射为极小高度，保证仍可见一小段柱体
const getZeroDisplayValue = (axisMax) => axisMax * 0.035

const create3DBarRenderer = (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 barHeight = Math.max(base[1] - top[1], MIN_BAR_PX)
  const x = base[0] - BAR_WIDTH / 2
  const y = base[1] - barHeight

  const rightFace = {
    type: 'polygon',
    shape: {
      points: [
        [x + BAR_WIDTH, y],
        [x + BAR_WIDTH + BAR_DEPTH_X, y - BAR_DEPTH_Y],
        [x + BAR_WIDTH + BAR_DEPTH_X, base[1] - BAR_DEPTH_Y],
        [x + BAR_WIDTH, base[1]]
      ]
    },
    style: {
      fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
        { offset: 0, color: isZero ? '#1f6f9f' : '#1a7ab8' },
        { offset: 1, color: isZero ? '#0a3048' : '#043558' }
      ])
    },
    silent: false
  }

  const frontFace = {
    type: 'rect',
    shape: {
      x,
      y,
      width: BAR_WIDTH,
      height: barHeight
    },
    style: {
      fill: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
        { offset: 0, color: isZero ? '#6ecdf0' : '#8ef0ff' },
        { offset: 0.45, color: isZero ? '#3aa8dc' : '#2dbdff' },
        { offset: 1, color: isZero ? '#0d4f7a' : '#0a5a96' }
      ])
    },
    silent: false
  }

  const topFace = {
    type: 'polygon',
    shape: {
      points: [
        [x, y],
        [x + BAR_DEPTH_X, y - BAR_DEPTH_Y],
        [x + BAR_WIDTH + BAR_DEPTH_X, y - BAR_DEPTH_Y],
        [x + BAR_WIDTH, y]
      ]
    },
    style: {
      fill: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
        { offset: 0, color: isZero ? '#9de6ff' : '#c8f6ff' },
        { offset: 1, color: isZero ? '#6ecdf0' : '#7ee8ff' }
      ])
    },
    silent: false
  }

  return {
    type: 'group',
    children: [rightFace, frontFace, topFace]
  }
}

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 || '接口返回异常')
    }

    chartData.value = {
      day: normalizePeriodData(result.data.day),
      week: normalizePeriodData(result.data.week),
      month: normalizePeriodData(result.data.month)
    }
    updateChart(true)
  } catch (error) {
    console.error('加载牦牛成交数据失败:', error)
  }
}

const initChart = () => {
  if (!chartRef.value) return

  chartInstance = echarts.init(chartRef.value)
  unbindChartBaseSync?.()
  unbindChartBaseSync = bindChartBaseSync(chartInstance, baseRef.value)
}

const buildChartOption = ({
  timeLabels,
  tradingSeries,
  orderSeries,
  yakAxisMax,
  orderAxisMax,
  tooltipTrading,
  tooltipOrders
}) => ({
  animation: false,
  tooltip: {
    trigger: 'axis',
    backgroundColor: 'rgba(8, 32, 68, 0.92)',
    borderColor: '#2dbdff',
    borderWidth: 1,
    padding: [10, 14],
    textStyle: {
      color: '#ffffff',
      fontSize: 13,
      fontFamily: 'Microsoft YaHei, sans-serif'
    },
    extraCssText: 'box-shadow: 0 0 16px rgba(45, 189, 255, 0.35); border-radius: 4px;',
    formatter(params) {
      const idx = params[0]?.dataIndex ?? 0
      const barItem = params.find((item) => item.seriesName === '牦牛交易数量')
      const lineItem = params.find((item) => item.seriesName === '成交订单数量')
      const barRaw = barItem?.value
      const yakVal = Array.isArray(barRaw)
        ? (Number(barRaw[1]) || 0)
        : (Number(barRaw) || tooltipTrading[idx] || 0)
      const orderVal = Number(lineItem?.value ?? tooltipOrders[idx] ?? 0) || 0
      let result = `<div style="font-weight:700;margin-bottom:6px;">${params[0].axisValue}</div>`
      result += `<div style="margin-top:4px;"><span style="display:inline-block;width:8px;height:8px;border-radius:2px;background:#4dd9ff;margin-right:6px;"></span>牦牛交易数量：${yakVal}头</div>`
      result += `<div style="margin-top:4px;"><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#ffd56a;margin-right:6px;"></span>成交订单数量：${orderVal}单</div>`
      return result
    }
  },
  grid: {
    left: 52,
    right: 52,
    bottom: selectedPeriod.value === 'week' ? CHART_GRID_BOTTOM_WEEK : CHART_GRID_BOTTOM,
    top: 42,
    containLabel: false
  },
  legend: {
    data: [
      { name: '牦牛交易数量', icon: 'rect' },
      { name: '成交订单数量', icon: 'circle' }
    ],
    top: 4,
    left: 'center',
    itemWidth: 12,
    itemHeight: 12,
    itemGap: 24,
    textStyle: {
      color: '#d8ecff',
      fontSize: 13,
      fontFamily: 'Microsoft YaHei, sans-serif'
    }
  },
  xAxis: {
    type: 'category',
    data: timeLabels,
    boundaryGap: true,
    axisLine: {
      show: false
    },
    axisTick: {
      show: false
    },
      axisLabel: {
        color: '#8ecfff',
        fontSize: 12,
        fontFamily: 'Microsoft YaHei, sans-serif',
        margin: 8,
        rotate: selectedPeriod.value === 'week' ? 35 : 0
      }
  },
  yAxis: [
    {
      type: 'value',
      name: '交易数量(头)',
      position: 'left',
      min: 0,
      max: yakAxisMax,
      splitNumber: 5,
      alignTicks: false,
      nameGap: 12,
      splitLine: {
        lineStyle: {
          color: 'rgba(80, 160, 220, 0.22)',
          type: 'dashed'
        }
      },
      axisLine: {
        show: false
      },
      axisTick: {
        show: false
      },
      axisLabel: {
        color: '#8ecfff',
        fontSize: 12,
        fontFamily: 'Microsoft YaHei, sans-serif'
      },
      nameTextStyle: {
        color: '#8ecfff',
        fontSize: 12,
        fontFamily: 'Microsoft YaHei, sans-serif',
        padding: [0, 0, 0, 0]
      }
    },
    {
      type: 'value',
      name: '订单数量(单)',
      position: 'right',
      min: 0,
      max: orderAxisMax,
      splitNumber: 5,
      alignTicks: false,
      nameGap: 12,
      splitLine: {
        show: false
      },
      axisLine: {
        show: false
      },
      axisTick: {
        show: false
      },
      axisLabel: {
        color: '#ffffff',
        fontSize: 12,
        fontFamily: 'Microsoft YaHei, sans-serif'
      },
      nameTextStyle: {
        color: '#ffffff',
        fontSize: 12,
        fontFamily: 'Microsoft YaHei, sans-serif'
      }
    }
  ],
  series: [
    {
      name: '牦牛交易数量',
      type: 'custom',
      yAxisIndex: 0,
      renderItem: create3DBarRenderer(yakAxisMax),
      data: tradingSeries.map((value, index) => [index, value]),
      encode: {
        x: 0,
        y: 1
      },
      z: 2
    },
    {
      name: '成交订单数量',
      type: 'line',
      yAxisIndex: 1,
      data: orderSeries,
      smooth: true,
      symbol: 'circle',
      symbolSize: 7,
      lineStyle: {
        color: '#f5b942',
        width: 3,
        shadowColor: 'rgba(245, 185, 66, 0.55)',
        shadowBlur: 10
      },
      itemStyle: {
        color: '#ffd56a',
        borderColor: '#ffffff',
        borderWidth: 2
      },
      z: 3
    }
  ]
})

const cancelGrowAnimation = () => {
  if (growAnimationFrame !== null) {
    cancelAnimationFrame(growAnimationFrame)
    growAnimationFrame = null
  }
}

const updateChart = (animateFromZero = false) => {
  if (!chartInstance) return

  cancelGrowAnimation()

  const periodData = chartData.value[selectedPeriod.value] || {
    labels: [],
    yakNumber: [],
    orderCount: []
  }
  const timeLabels = periodData.labels
  const tradingData = periodData.yakNumber
  const orderData = periodData.orderCount

  if (!timeLabels.length) {
    return
  }

  const yakAxisMax = calcAxisMax(tradingData, 1.25)
  const orderAxisMax = calcAxisMax(orderData, 2.4)
  const zeroTrading = tradingData.map(() => 0)
  const zeroOrders = orderData.map(() => 0)

  const baseConfig = {
    timeLabels,
    yakAxisMax,
    orderAxisMax,
    tooltipTrading: tradingData,
    tooltipOrders: orderData
  }

  const render = (tradingSeries, orderSeries, replaceAll = true) => {
    chartInstance.setOption(
      buildChartOption({
        ...baseConfig,
        tradingSeries,
        orderSeries
      }),
      replaceAll
    )
  }

  const renderSeriesFrame = (tradingSeries, orderSeries) => {
    chartInstance.setOption({
      series: [
        {
          name: '牦牛交易数量',
          data: tradingSeries.map((value, index) => [index, value])
        },
        {
          name: '成交订单数量',
          data: orderSeries
        }
      ]
    }, false)
  }

  const playGrowAnimation = () => {
    render(zeroTrading, zeroOrders, true)

    const startTime = performance.now()
    const tick = (now) => {
      const progress = Math.min((now - startTime) / GROW_ANIMATION_MS, 1)
      const ratio = easeOutCubic(progress)
      const currentTrading = tradingData.map((v) => (Number(v) || 0) * ratio)
      const currentOrders = orderData.map((v) => (Number(v) || 0) * ratio)

      renderSeriesFrame(currentTrading, currentOrders)

      if (progress < 1) {
        growAnimationFrame = requestAnimationFrame(tick)
        return
      }

      growAnimationFrame = null
      render(tradingData, orderData, false)
    }

    growAnimationFrame = requestAnimationFrame(tick)
  }

  if (!animateFromZero) {
    render(tradingData, orderData, true)
    return
  }

  playGrowAnimation()
}

watch(selectedPeriod, () => {
  updateChart(true)
})

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

onMounted(async () => {
  initChart()
  await loadData()
  window.addEventListener('resize', handleResize)
  window.addEventListener('dataReloaded', loadData)
})

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

<style scoped>
.chart-wrapper {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 0 4px;
  overflow: visible;
}

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

.chart-base {
  display: block;
  width: 100%;
  height: 90px;
  object-fit: fill;
  flex-shrink: 0;
  pointer-events: none;
  user-select: none;
  margin-top: -26px;
}
</style>
