<template>
  <div ref="chartRef" :style="{ width: width, height: height }"></div>
</template>

<script setup>
import { ref, watch, onMounted, nextTick } from 'vue';
import { merge, cloneDeep } from 'lodash';
import { useEcharts } from '@/hooks/useEcharts';

defineOptions({ name: 'NewHyalineCake' });

let selectedIndex = null;
let hoveredIndex = null;

const props = defineProps({
  chartData: {
    type: Array,
    default: () => [
      { name: '项目一', value: 60 },
      { name: '项目二', value: 44 },
      { name: '项目三', value: 32 },
    ],
  },
  option: {
    type: Object,
    default: () => ({
      k: 1, // 控制内外径关系的系数，1 表示无内径，值越小内径越大
      itemGap: 0.1, // 扇形的间距
      itemHeight: 120, // 扇形高度（影响z轴拉伸程度）
      autoItemHeight: 0, // 自动计算扇形高度时使用的系数（>0时 itemHeight 失效，使用 autoItemHeight * value ）
      opacity: 0.6, // 透明度设置
      legendSuffix: '', // 图例后缀
      // TODO series
    }),
  },
  width: {
    type: String,
    default: '100%',
  },
  height: {
    type: String,
    default: '100%',
  },
});

const emit = defineEmits(['click']);

const chartRef = ref(null);
const { setOptions, getInstance } = useEcharts(chartRef);

const chartOption = ref({});

/**
 * 获取 parametric 曲面方程
 * @param {Number} startRatio 起点弧度
 * @param {Number} endRatio 终点弧度
 * @param {Boolean} isSelected 是否选中
 * @param {Boolean} isHovered 是否高亮
 * @param {Number} k 饼图内径/外径的占比
 * @param {Number} h 饼图高度
 * @param {Array} offsetZ 浮动系数
 * @return {Object} parametric 曲面方程
 */
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h, offsetZ = 0) {
  // console.log('getParametricEquation params :>> ', startRatio, endRatio, isSelected, isHovered, k, h, offsetZ);
  const midRatio = (startRatio + endRatio) / 2;
  const startRadian = startRatio * Math.PI * 2;
  const endRadian = endRatio * Math.PI * 2;
  const midRadian = midRatio * Math.PI * 2;

  // 如果整个饼只剩一个扇形，则不实现选中效果
  if (startRatio === 0 && endRatio === 1) {
    isSelected = false;
  }
  // k 取默认值 1/3（扇形内径/外径），如果传入 k 则使用传入值
  k = typeof k !== 'undefined' ? k : 1 / 3;

  // 计算选中效果的偏移量（基于扇形中心角度）
  const offsetX = isSelected ? Math.cos(midRadian) * props.option.itemGap : 0;
  const offsetY = isSelected ? Math.sin(midRadian) * props.option.itemGap : 0;
  // 计算高亮时的放大比例（未高亮时为1）
  const hoverRate = isHovered ? 1.05 : 1;

  return {
    u: {
      // u 参数控制饼的周向角度：从 -π 到 3π，可以完整绘制一圈
      min: -Math.PI,
      max: Math.PI * 3,
      step: Math.PI / 32,
    },
    v: {
      // v 从 0 - 2π 参数控制扇形的径向方向（厚度方向）
      min: 0,
      max: Math.PI * 2,
      step: Math.PI / 20,
    },
    x(u, v) {
      // 如果在起始弧度之前，保持扇形起点的半径
      if (u < startRadian) {
        return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
      }
      // 如果在结束弧度之后，保持扇形终点的半径
      if (u > endRadian) {
        return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
      }
      // 扇形中间部分正常绘制
      return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
    },
    y(u, v) {
      if (u < startRadian) {
        return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate;
      }
      if (u > endRadian) {
        return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate;
      }
      return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
    },
    z(u, v) {
      // 在 u < -π/2 时，防止出现不必要的翻转，直接使用基本正弦
      if (u < -Math.PI * 0.5) {
        return Math.sin(u) + offsetZ * h * 0.1;
      }
      // 在 u > 2.5π 时，处理扇形尾部厚度
      if (u > Math.PI * 2.5) {
        return Math.sin(u) * h * 0.1 + offsetZ * h * 0.1;
      }
      // 正常情况下，z 根据 v 参数控制上下表面的高度差
      // 当前图形的高度是Z根据h（每个value的值决定的）
      return Math.sin(v) > 0 ? 1 * h * 0.1 + offsetZ * h * 0.1 : -1 + offsetZ * h * 0.1;
    },
  };
}

/**
 * 获取 3D 饼图的配置项
 * @param {Array} pieData 饼图数据
 * @param {Number} internalDiameterRatio 饼图内径/外径的占比
 * @return {Object} 配置项
 */
function getPie3D(pieData, internalDiameterRatio) {
  const series = [];
  let sumValue = 0;
  let startValue = 0;
  let endValue = 0;
  const legendData = [];

  const k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3;

  // 构建每个扇形的 series 数据
  for (let i = 0; i < pieData.length; i += 1) {
    sumValue += pieData[i].value;

    const seriesItem = {
      name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
      type: 'surface',
      parametric: true,
      wireframe: { show: false },
      pieData: pieData[i],
      pieStatus: {
        selected: true,
        hovered: false,
        k,
      },
      label: {
        show: true,
        formatter: () => `${pieData[i].value}`, // 直接显示 value
        position: 'outside', // 标签位置
        distance: 10, // 距扇形边缘的距离
        color: '#fff', // 根据背景色调整
        fontSize: 14,
      },
    };
    if (typeof pieData[i].itemStyle !== 'undefined') {
      const { itemStyle } = pieData[i];
      typeof pieData[i].itemStyle.color !== 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null;
      typeof pieData[i].itemStyle.opacity !== 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;

      seriesItem.itemStyle = itemStyle;
    }
    series.push(seriesItem);
  }
  // 使用上一次遍历时，计算出的数据和 sumValue，调用 getParametricEquation 函数，
  // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation，也就是实现每一个扇形。
  // console.log(series);
  for (let i = 0; i < series.length; i += 1) {
    endValue = startValue + series[i].pieData.value;

    const z = series[i]?.pieData?.floatZ ?? 0;
    series[i].pieData.startRatio = startValue / sumValue;
    series[i].pieData.endRatio = endValue / sumValue;
    series[i].parametricEquation = getParametricEquation(
      series[i].pieData.startRatio,
      series[i].pieData.endRatio,
      series[i].pieStatus.selected,
      series[i].pieStatus.hovered,
      k,
      props.option.autoItemHeight > 0 ? props.option.autoItemHeight * series[i].pieData.value : props.option.itemHeight,
      z
    );

    startValue = endValue;

    legendData.push(series[i].name);
  }
  // 基础配置：坐标轴、视角、图例、提示框等
  // 准备待返回的配置项，把准备好的 legendData、series 传入。
  const option = {
    legend: {
      type: 'scroll',
      show: true,
      right: '5%',
      top: '25%',
      orient: 'vertical',
      icon: 'circle',
      itemHeight: 12,
      itemWidth: 12,
      itemGap: 10,
      textStyle: {
        color: '#fff',
        fontSize: 14,
        fontWeight: '400',
      },
      formatter: (name) => {
        if (props.chartData.length) {
          const item = props.chartData.filter((item) => item.name === name)[0];
          return `  ${name}      ${item.value}${props.option.legendSuffix ?? ''}`;
        }
      },
    },
    // animation: false,
    tooltip: {
      formatter: (params) => {
        if (params.seriesName !== 'mouseoutSeries') {
          return `${
            params.seriesName
          }<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${
            params.color
          };"></span>${option.series[params.seriesIndex].pieData.value}`;
        }
        return '';
      },
    },
    xAxis3D: {
      min: -1,
      max: 1,
    },
    yAxis3D: {
      min: -1,
      max: 1,
    },
    zAxis3D: {
      min: -1,
      max: 1,
    },
    grid3D: {
      show: true,
      boxHeight: 5,
      top: '-20%',
      viewControl: {
        // 3d效果可以放大、旋转等，请自己去查看官方配置
        alpha: 35,
        // beta: 30,
        rotateSensitivity: 1,
        zoomSensitivity: 0,
        panSensitivity: 0,
        autoRotate: true,
        distance: 150,
      },
      // 后处理特效可以为画面添加高光、景深、环境光遮蔽（SSAO）、调色等效果。可以让整个画面更富有质感。
      postEffect: {
        // 配置这项会出现锯齿，请自己去查看官方配置有办法解决
        enable: false,
        bloom: {
          enable: true,
          bloomIntensity: 0.1,
        },
        SSAO: {
          enable: true,
          quality: 'medium',
          radius: 2,
        },
        // temporalSuperSampling: {
        //   enable: true,
        // },
      },
    },
    series,
  };
  return option;
}

// 监听 mouseover，近似实现高亮（放大）效果
function handleMouseover(params) {
  // console.log('mouseover');
  const idx = params.seriesIndex;
  const series = chartOption.value.series;

  // 如果当前扇形已经高亮，不做任何操作
  if (hoveredIndex === idx) return;

  // 1. 取消上一个高亮
  if (hoveredIndex !== null && hoveredIndex >= 0 && series[hoveredIndex]) {
    updateSeriesHover(hoveredIndex, false);
  }

  // 2. 设置当前扇形高亮（排除辅助环）
  if (params.seriesName !== 'mouseoutSeries' && series[idx]) {
    updateSeriesHover(idx, true);
    hoveredIndex = idx;
  } else {
    hoveredIndex = null;
  }

  // 3. 重新渲染
  setOptions(chartOption.value);
}

function handleGlobalout() {
  const idx = hoveredIndex;
  if (idx !== null && idx >= 0 && chartOption.value.series[idx]) {
    updateSeriesHover(idx, false);
    hoveredIndex = null;
    setOptions(chartOption.value);
  }
}

// 抽取高亮／取消高亮的公共逻辑
function updateSeriesHover(index, toHover) {
  const item = chartOption.value.series[index];
  // 安全取 pieStatus，fallback 到空对象
  const status = item.pieStatus || {};
  const isSelected = !!status.selected;
  const start = item.pieData?.startRatio ?? 0;
  const end = item.pieData?.endRatio ?? 1;
  const k = typeof status.k === 'number' ? status.k : 1 / 3;
  // 计算 newHeight：如果 hover，则加 5，否则按原 height
  const baseHeight = props.option.autoItemHeight > 0 ? props.option.autoItemHeight : props.option.itemHeight;
  const newHeight = toHover ? baseHeight + 5 : baseHeight;
  // 更新 parametricEquation
  item.parametricEquation = getParametricEquation(start, end, isSelected, toHover, k, newHeight);
  // 更新状态
  item.pieStatus = {
    ...status,
    hovered: toHover,
  };
}

// 初始化图表
function initChart() {
  // 生成基础的3D饼图配置(也就是默认的配置)
  const baseOption = getPie3D(props.chartData, props.option.k);
  // 合并用户配置，确保不修改原始对象
  // const finalOption = Object.assign({}, baseOption, cloneDeep(props.option || {}));
  const finalOption = merge({}, baseOption, cloneDeep(props.option || {}));
  chartOption.value = finalOption;
  // 设置图表配置
  setOptions(chartOption.value);
  // 等待 DOM + ECharts 初始化完成
  nextTick(() => {
    const chart = getInstance();
    if (!chart) {
      console.warn('ECharts 实例未初始化，事件绑定失败');
      return;
    }

    // 避免重复绑定事件
    // chart.off('click');
    chart.off('mouseover');
    chart.off('globalout');

    // chart.on('click', handleClick);
    chart.on('mouseover', handleMouseover);
    chart.on('globalout', handleGlobalout);
  });
}
function handleClick(params) {
  // 如果点击的是透明辅助环，则忽略
  if (params.seriesName === 'mouseoutSeries') return;

  const optionVal = chartOption.value;
  const series = optionVal.series;
  const idx = params.seriesIndex;

  // 新的选中状态（取反）
  const isSelected = !series[idx].pieStatus.selected;
  const isHovered = series[idx].pieStatus.hovered;
  const k = series[idx].pieStatus.k;
  const startRatio = series[idx].pieData.startRatio;
  const endRatio = series[idx].pieData.endRatio;
  const h = series[idx].pieData.value;

  // 如果之前有其他扇形被选中，取消其选中状态
  if (selectedIndex !== null && selectedIndex !== idx) {
    const prev = series[selectedIndex];
    prev.parametricEquation = getParametricEquation(
      prev.pieData.startRatio,
      prev.pieData.endRatio,
      false,
      false,
      prev.pieStatus.k,
      prev.pieData.value
    );
    prev.pieStatus.selected = false;
    selectedIndex = null;
  }

  // 设置当前扇形的选中/取消选中状态
  series[idx].parametricEquation = getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h);
  series[idx].pieStatus.selected = isSelected;

  selectedIndex = isSelected ? idx : null;
  setOptions(optionVal);
  emit('click', params);
}

// 组件挂载后绑定事件
onMounted(() => {
  initChart();
});

// 监听数据或配置变更，重新初始化图表
watch(
  [() => props.chartData, () => props.option],
  () => {
    if (props.chartData && props.chartData.length) {
      initChart();
    }
  },
  { immediate: true, deep: true }
);
</script>

<style scoped>
/* 可根据需要自定义图表容器样式 */
</style>
