<template>
  <div class="echart_content">
    <div class="is_show_all" v-if="props.showCheckBox">
      <el-checkbox
        v-model="isShowAll"
        :indeterminate="isIndeterminate"
        @change="ToggleChangeAll"
      >
        显示全部
      </el-checkbox>
    </div>
    <!-- 横向堆叠条形图 -->
    <div
      ref="barStackChart"
      class="chart_bar_stack"
      v-if="props.data.length < 15"
    ></div>
    <div ref="barStackChart" class="chart_bar_stacks" v-else></div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue';
import * as echarts from 'echarts';
import throttle from 'lodash/throttle';

// ================= Props & Emits =================
interface Props {
  showCheckBox?: boolean;
  data?: any[][];
  color?: string[];
  isClick?: boolean;
  tooltipData?: any[];
}

const props = withDefaults(defineProps<Props>(), {
  showCheckBox: true,
  data: () => [],
  color: () => [],
  isClick: false,
  tooltipData: () => [],
});

const emit = defineEmits<{
  (e: 'HandleChartClick', index: number, xName: string): void;
}>();

// ================= Refs & State =================
const barStackChart = ref<HTMLElement | null>(null);
const myChart = ref<echarts.ECharts | null>(null);

const isShowAll = ref(true);
const isIndeterminate = ref(false);
const legendSelected = ref<Record<string, boolean>>({});
const legenSelectList = ref<string[]>(props.data?.[0]?.slice(1) as string[] || []);
const legenAllList = ref<string[]>(props.data?.[0]?.slice(1) as string[] || []);
const chartHeight = ref(400);

// ================= Core Methods =================
const LoadEchart = () => {
  if (myChart.value) {
    myChart.value.dispose();
  }
  if (!barStackChart.value) return;

  myChart.value = echarts.init(barStackChart.value, null, {
    devicePixelRatio: 2,
  });

  //定义数据集
  const dataset = {
    source: props.data,
  };

  legenAllList.value = (props.data?.[0]?.slice(1) as string[]) || [];

  // 初始化 legendSelected 为所有图例项显示
  legendSelected.value = (dataset.source[0] as string[]).slice(1).reduce((acc: Record<string, boolean>, dim) => {
    acc[dim] = false; // 默认不显示
    return acc;
  }, {});

  legenSelectList.value.forEach((item) => {
    legendSelected.value[item] = true;
  });

  isShowAll.value = legenSelectList.value.length === legenAllList.value.length;
  isIndeterminate.value =
    legenSelectList.value.length > 0 &&
    legenSelectList.value.length < legenAllList.value.length;

  let colors = props.color; //按顺序显示的20个颜色值
  if (props.data && props.data.length > 10) {
    chartHeight.value = 600;
  }

  //计算每个y轴的高度
  let totalHeight = barStackChart.value.clientHeight;
  //计算每个柱的宽度 （图表宽度-左右边距）/ x轴数据长度
  let singleSeriesWidth = Math.ceil((totalHeight - 97) / (props.data?.length || 1));

  const barMinWidth = 20; // 最小柱子宽度 加上两边间距的宽度
  const dataZoomNum = Math.floor((totalHeight - 97) / (barMinWidth * 1)); // 当前区域内可显示的柱子的数量
  const dataZoomEnd = Math.floor((100 / (props.data?.length || 1)) * dataZoomNum); // 当前区域内可显示柱子的占比

  const dataZoom = {
    start: 0,
    end: dataZoomEnd,
    type: 'slider',
    show: true,
    borderColor: 'transparent',
    borderCap: 'round',
    yAxisIndex: [0],
    width: 8,
    right: 20,
    top: 20,
    bottom: 20,
    fillerColor: 'transparent',
    zoomLock: true,
    handleSize: '0',
    handleStyle: {
      color: '#b8b8b8',
      borderWidth: 2,
    },
    backgroundColor: 'transparent',
    showDataShadow: false,
    showDetail: false,
    filterMode: 'filter',
  };

  const option: any = {
    dataset: dataset,
    tooltip: {
      triggerOn: 'mousemove',
      confine: true,
      extraCssText:
        'border-radius: 4px;padding:5px 0px 5px 5px;white-space:normal;word-warp:break-word;max-width: 400px;',
      enterable: true,
      formatter: (params: any) => {
        const seriesName = params.name;
        let tooltip = `<div class='tooltip_content'>`;
        let title = seriesName;
        tooltip += `<div class='tooltip_title'>${title}</div>`;
        let rateLabel = (dataset.source[0] as string[]).slice(1);

        for (let i = 0; i < rateLabel.length; i++) {
          let labelName = rateLabel[i];
          let rate = params?.value?.[i + 1] || '-';
          let list: any[] = [],
            rateNum = '',
            isShowTip = props.tooltipData?.length > 0;

          if (props.tooltipData?.length > 0) {
            const dataIndex = params.dataIndex;
            const itemTooltip = props.tooltipData?.[dataIndex]?.[i] ?? '';
            list = itemTooltip?.list ?? [];
            rateNum = itemTooltip?.rateNum ?? '-';
          }

          if (isShowTip) {
            tooltip += `<div class='tooltip_student'><span class='tooltip_rect_icon' style='background:${colors[i]}'></span>${labelName}：${rate == '-' ? '-' : rate + '%'}，${rateNum}人</div>`;
            if (i == rateLabel.length - 1) {
              for (let j = 0; j < list.length; j++) {
                tooltip += `<div class='tooltip_student'><span class='tooltip_rect_icon' style='background:#CCCCCC'></span>${list[j].label}：${list[j].value}</div>`;
              }
            }
          } else {
            tooltip += `<div class='tooltip_student'><span class='tooltip_rect_icon' style='background:${colors[i]}'></span>${labelName}：${rate == '-' ? '-' : rate + '%'}</div>`;
          }
        }

        tooltip += `</div>`;
        return tooltip;
      },
    },
    legend: {
      show: true,
      top: '6px',
      left: '110px',
      itemGap: 20,
      itemHeight: 10,
      itemWidth: 20,
      textStyle: { fontSize: 12, color: '#333' },
      selectedMode: true,
      selected: legendSelected.value,
      formatter: (name: string) => {
        return name;
      },
    },
    grid: {
      top: 40,
      left: 0,
      right: 40,
      bottom: 0,
      containLabel: true,
    },
    dataZoom: singleSeriesWidth < barMinWidth ? dataZoom : null,
    xAxis: {
      type: 'value',
      axisLabel: {
        formatter: '{value}%',
        interval: 0,
        fontSize: 14,
        color: '#666',
        fontWeight: 400,
      },
      axisTick: {
        lineStyle: {
          color: 'red',
          type: 'dashed',
          width: 1,
        },
      },
    },
    yAxis: {
      type: 'category',
      axisLabel: {
        interval: 0,
        formatter: '{value}',
        textStyle: {
          fontSize: 14,
          color: '#666',
          fontWeight: 400,
        },
      },
      inverse: true,
    },
    series: (dataset.source[0] as string[]).slice(1).map(function (dim, index) {
      const color = colors[index];
      return {
        type: 'bar',
        stack: 'total',
        barMaxWidth: 50,
        barMinWidth: 14,
        label: {
          show: true,
          position: 'inside',
          color: '#fff',
          fontSize: dataset.source.length > 10 ? 12 : 14,
          formatter: function (params: any) {
            if (params.value[params.seriesIndex + 1] == 0) {
              return '';
            } else {
              return params.value[params.seriesIndex + 1] + '%';
            }
          },
        },
        itemStyle: {
          color: color,
        },
        emphasis: {
          focus: 'series',
        },
      };
    }),
  };

  myChart.value.setOption(option);
  myChart.value.on('legendselectchanged', HandleLegendSelectChanged);

  if (props.isClick) {
    const yAxisDataLength = (props.data?.length || 1) - 1;
    const gridRect = myChart.value
      .getModel()
      .getComponent('grid').coordinateSystem.getRect();

    const singleLabelHeight = gridRect.height / yAxisDataLength;

    myChart.value.on('click', (params: any) => {
      if (params.seriesType === 'bar') {
        let pixelPosition = myChart.value!.convertToPixel(
          { yAxisIndex: 0 },
          params.name,
        );
        myChart.value!.setOption({
          graphic: {
            id: 'highlight-box',
            type: 'rect',
            shape: {
              y: pixelPosition - singleLabelHeight / 2,
              x: gridRect.x,
              width: gridRect.width,
              height: singleLabelHeight,
            },
            style: {
              fill: params.color + '30',
            },
          },
        });
        let index = params.dataIndex;
        let xName = params.name;
        emit('HandleChartClick', index, xName);
      }
    });

    let defaultPixelPosition = myChart.value.convertToPixel(
      { yAxisIndex: 0 },
      props.data![1][0],
    );
    myChart.value.setOption({
      graphic: {
        id: 'highlight-box',
        type: 'rect',
        shape: {
          y: defaultPixelPosition - singleLabelHeight / 2,
          x: gridRect.x,
          height: singleLabelHeight,
          width: gridRect.width,
        },
        style: {
          fill: 'rgba(84,112,198,0.1)',
        },
      },
    });
  }
};

// ================= Event Handlers =================
const HandleLegendSelectChanged = (params: any) => {
  const selected = params.selected;
  legenSelectList.value = [];
  for (let i = 0; i < legenAllList.value.length; i++) {
    if (selected[legenAllList.value[i]] == true) {
      legenSelectList.value.push(legenAllList.value[i]);
    }
  }
  isShowAll.value = legenSelectList.value.length === legenAllList.value.length;
  isIndeterminate.value =
    legenSelectList.value.length > 0 &&
    legenSelectList.value.length < legenAllList.value.length;
};

const handleResize = throttle(() => {
  nextTick(() => {
    myChart.value?.resize();
  });
}, 500);

const ToggleChangeAll = () => {
  if (isShowAll.value) {
    legenSelectList.value = (props.data?.[0]?.slice(1) as string[]) || [];
  } else {
    legenSelectList.value = [];
  }
  LoadEchart();
};

// ================= Watch =================
watch(
  () => props.data,
  () => {
    LoadEchart();
  },
  { deep: true },
);

// ================= Lifecycle =================
onMounted(() => {
  window.addEventListener('resize', handleResize);
  LoadEchart();
});

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize);
  if (myChart.value) {
    myChart.value.dispose();
  }
});
</script>

<style lang="scss" scoped>
.chart_bar_stack {
  min-height: 400px;
}
.chart_bar_stacks {
  height: 600px;
}
</style>