<template>
  <div class="echart_content">
    <div class="is_show_all" v-if="showCheckBox">
      <el-checkbox
        v-model="checkAll"
        :indeterminate="isIndeterminate"
        @change="handleCheckAllChange"
      >
        显示全部
      </el-checkbox>
    </div>
    <div class="title_right" v-if="selectBarNum == 1">
      <div
        :class="['right_item', { item_active: selectValue == item.value }]"
        v-for="item in selectOptions"
        :key="item.value"
        @click="ChangeOrder(item.value)"
      >
        {{ item.label }}
      </div>
    </div>
    <!-- 多柱状图 -->
    <div ref="barCharts" class="chart_width"></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";
import { getScorePerformanceAnalysis } from "@/utils/common";

// ================= Props & Emits =================
interface Props {
  showCheckBox?: boolean;
  color?: string[];
  data?: any[][];
  legendList?: string[];
  title?: string;
  unit?: string;
  isClick?: boolean;
  tooltipData?: any[];
  fiveTooltipData?: any[];
  showSortSelectbox?: boolean;
  legendWidth?: number | string;
  gridRight?: number;
  yInverse?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  showCheckBox: true,
  color: () => [],
  data: () => [],
  legendList: () => [],
  title: "",
  unit: "",
  isClick: false,
  tooltipData: () => [],
  fiveTooltipData: () => [],
  showSortSelectbox: false,
  legendWidth: "auto",
  gridRight: 60,
  yInverse: false,
});

const emit = defineEmits<{
  (e: "HandleChartClick", index: number, name: string): void;
  (
    e: "ChangeChartOrder",
    val: string,
    legendData: string[],
    selectedIndex: number | string,
  ): void;
}>();

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

const selectValue = ref("1");
const selectOptions = [
  { value: "1", label: "默认" },
  { value: "2", label: "升序" },
  { value: "3", label: "降序" },
];

const selectBarNum = ref(0);
const checkAll = 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 dataZoomEnd = ref(0);
const dataZoomNum = ref(5);
const currentIndex = ref(0);
const isShowDataZoom = ref(false);

// ================= Helper Methods =================
const getReverseData = () => {
  const dataList = (props.data?.slice(1) || []) as any[][];
  let data: any[] = [];
  dataList.forEach((item) => {
    const arr = item?.slice(1) ?? [];
    data.push(...arr);
  });
  const safeData = Array.isArray(data)
    ? data.filter((item) => !isNaN(Number(item)))
    : [];
  if (safeData.length === 0) return { reverseData: [], dataMin: 0, dataMax: 0 };

  const dataMin = Math.min(...safeData.map(Number));
  const dataMax = Math.max(...safeData.map(Number));
  let reverseData: any[] = [];

  dataList.forEach((item) => {
    const arr = item ?? [];
    let arrList: any[] = [];
    arr.forEach((val, index) => {
      if (index > 0) {
        const valNumber = Number(isNaN(Number(val)) ? 0 : val);
        if (valNumber > 0) {
          arrList.push(
            dataMax - Number(isNaN(Number(val)) ? 0 : val) + dataMin,
          );
        } else {
          if (dataMax > dataMin) arrList.push(dataMin);
          else arrList.push(0);
        }
      } else {
        arrList.push(val);
      }
    });
    reverseData.push(arrList);
  });
  reverseData.unshift(...(props.data?.slice(0, 1) || []));
  return { reverseData, dataMin, dataMax };
};

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

  echart.value = echarts.init(barCharts.value, null, { devicePixelRatio: 2 });

  const { reverseData, dataMin, dataMax } = getReverseData();
  const dataset = { source: props.yInverse ? reverseData : props.data };

  let constColorAll = getScorePerformanceAnalysis();
  if (props.color && props.color.length > 0) {
    constColorAll = props.color;
  }
  const unit = props.unit || "";

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

  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;
  });

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

  const numSeries = Object.keys(legendSelected.value).filter(
    (key) => legendSelected.value[key],
  ).length;
  if (props.showSortSelectbox) {
    selectBarNum.value = numSeries;
  }

  const dataxCount = (props.data?.length || 1) - 1;
  const totalWidth = barCharts.value.clientWidth;
  const barMinWidth = 26;

  dataZoomNum.value = Math.floor(
    (totalWidth - 140) / (barMinWidth * numSeries),
  );
  const echartWidth = (dataset.source.length - 1) * barMinWidth * numSeries;
  let singleSeriesWidth = Math.ceil((totalWidth - 140) / dataxCount);

  dataZoomEnd.value = Math.floor(
    (100 / (dataset.source.length - 1)) * dataZoomNum.value,
  );
  isShowDataZoom.value = echartWidth > totalWidth;

  if (isShowDataZoom.value) {
    singleSeriesWidth = 25 * legenSelectList.value.length;
  }

  const dataZoom = {
    start: 0,
    end: dataZoomEnd.value,
    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",
    pageIconColor: "#999",
    pageIconInactiveColor: "#ccc",
  };

  const option: any = {
    dataset: dataset,
    tooltip: {
      axisPointer: { type: dataset.source.length < 3 ? "none" : "shadow" },
      trigger: "axis",
      triggerOn: "mousemove | click",
      renderModel: "html",
      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) => {
        let tooltip = `<div class='tooltip_content'>`;
        let newData = dataset.source.slice(1);
        let xAxisName = newData[params[0].dataIndex][0];
        let title = xAxisName;
        let teachClassName = "";
        if (props.tooltipData && props.tooltipData.length > 0) {
          teachClassName =
            props.tooltipData[params[0]?.seriesIndex]?.[params[0]?.dataIndex]
              ?.teacherName || "";
        }
        tooltip += `<div class='tooltip_title'>${title} ${teachClassName}</div>`;

        params.forEach((item: any, index: number) => {
          let className = item.seriesName;
          let rate =
            item.data.value !== undefined
              ? item.data.value
              : item.data[item.seriesIndex + 1];
          let studentNames = "";

          if (props.tooltipData && props.tooltipData.length > 0) {
            let tooltipItem =
              props.tooltipData?.[item.seriesIndex]?.[item.dataIndex] || "";
            tooltip += `<div class='tooltip_student'><div class='tooltip_rect_icon' style='background:${item.color}'></div>${tooltipItem.name}：${tooltipItem.value}</div>`;
            let studentNameList =
              props.tooltipData[item.seriesIndex]?.[
                item.dataIndex
              ]?.studentName?.map(
                (student: any) => student?.studentName ?? "",
              ) || [];
            studentNames = studentNameList.join("、");
            if (studentNames) {
              tooltip += `<div class='tooltip_student'><span class='tooltip_zw'> </span>  (${studentNames})</div>`;
            }
          } else if (
            props.fiveTooltipData &&
            props.fiveTooltipData.length > 0
          ) {
            let list: any[] = [],
              rateNum = "";
            const dataIndex = item.dataIndex;
            const itemTooltip =
              props.fiveTooltipData?.[dataIndex]?.[item.seriesIndex] ?? "";
            list = itemTooltip?.list ?? [];
            rateNum = itemTooltip?.rateNum ?? "-";
            tooltip += `<div class='tooltip_student'><span class='tooltip_rect_icon' style='background:${item.color}'></span>${className}${props.title}：${rate == "-" ? "-" : rate + unit}，${rateNum}人</div>`;
            if (index == params.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 if (
            props.yInverse &&
            (!props.tooltipData || props.tooltipData.length == 0)
          ) {
            const sourceData = props.data || [];
            const dataIndex = item.dataIndex + 1;
            const seriesIndex = item.seriesIndex + 1;
            let rateVal = sourceData?.[dataIndex]?.[seriesIndex] ?? "-";
            tooltip += `<div class='tooltip_student'><span class='tooltip_rect_icon' style='background:${item.color}'></span>${className}${props.title}：${rateVal ? rateVal + unit : "-"}</div>`;
          } else {
            tooltip += `<div class='tooltip_student'><span class='tooltip_rect_icon' style='background:${item.color}'></span>${className}${props.title}：${rate ? rate + unit : "-"}</div>`;
          }
        });
        tooltip += `</div>`;
        return tooltip;
      },
    },
    legend: {
      show: true,
      top:'4px',
      left: props.showCheckBox ? "110px" : 0,
      width:
        props.showSortSelectbox && selectBarNum.value == 1
          ? "78%"
          : props.legendWidth,
      itemGap: 20,
      itemHeight: 10,
      itemWidth: 20,
      textStyle: { fontSize: 12, color: "#333" },
      selectedMode: true,
      selected: legendSelected.value,
      type: "scroll",
      pageButtonItemGap: 5,
      pageButtonPosition: "end",
      orient: "horizontal",
      alignTo: "none",
      pageIconColor: "#999",
      pageIconInactiveColor: "#ccc",
      formatter: (name: string) => name.replace(/g/g, "G"),
    },
    dataZoom: isShowDataZoom.value ? dataZoom : [],
    grid: {
      top: 50,
      left: 40,
      right: props.gridRight,
      bottom: isShowDataZoom.value ? 15 : 0,
      containLabel: true,
    },
    yAxis: {
      type: "value",
      axisLabel: {
        formatter: (val: number) => {
          let value = props.yInverse ? dataMax - val + dataMin : val;
          value = props.yInverse
            ? Number(value.toFixed(0))
            : Number(value.toFixed(1));
          return unit == "%" ? value + unit : value;
        },
        fontSize: 14, color: "#666", fontWeight: 400
      },
      splitArea: { show: true, areaStyle: { color: ["#fafafa", "#ffffff"] } },
      min: props.yInverse ? dataMin : null,
      max: props.yInverse ? dataMax : null,
    },
    xAxis: {
      type: "category",
      triggerEvent: true,
      axisTick: { alignWithLabel: true },
      axisLabel: {
        interval: 0,
        rotate: singleSeriesWidth < 80 ? 45 : 0,
        fontSize: 14, color: "#666", fontWeight: 400,
        formatter: function (value: any) {
          let newData = dataset.source.slice(1);
          let xAxisName = newData[value]?.[0];
          return xAxisName;
        },
      },
    },
    series: (dataset.source[0] as string[]).slice(1).map(function (dim, index) {
      const seriesData = dataset.source
        .slice(1)
        .map((dataRow: any[], dataIndex: number) => {
          const value = dataRow[index + 1];
          return {
            value: value,
            label: {
              show: true,
              position: value >= 0 ? "top" : "bottom",
              color: "#666",
              fontSize: 12,
              formatter: function () {
                return (
                  (props.yInverse
                    ? props.data?.[dataIndex + 1]?.[index + 1]
                    : value) + unit
                );
              },
            },
          };
        });

      return {
        type: "bar",
        name: dim,
        barGap: 0,
        barCategoryGap: 30,
        barMaxWidth: 50,
        barMinWidth: 20,
        itemStyle: { color: constColorAll[index], backgroundColor: "red" },
        data: seriesData,
        label: {
          show: true,
          color: "#666",
          fontSize: 12,
          formatter: function (params: any) {
            return (
              (props.yInverse
                ? props.data?.[params.dataIndex + 1]?.[params.seriesIndex + 1]
                : params.value[params.seriesIndex + 1]) + unit
            );
          },
        },
      };
    }),
  };

  if (echartWidth > totalWidth) {
    option.dataZoom = dataZoom;
  }

  echart.value.setOption(option, true);

  if (props.isClick) {
    const gridRect = echart.value
      .getModel()
      .getComponent("grid")
      .coordinateSystem.getRect();
    let singleLabelWidth = gridRect.width / dataxCount;
    let height = 0,
      itemBarWidth = 20;

    if (isShowDataZoom.value) {
      singleLabelWidth = numSeries * 26 - 10;
    } else {
      itemBarWidth =
        (singleLabelWidth * 0.7) / numSeries > 50
          ? 50
          : (singleLabelWidth * 0.7) / numSeries;
    }

    const barSeries = (dataset.source[0] as string[])
      .slice(1)
      .map(function (dim, index) {
        const seriesData = dataset.source
          .slice(1)
          .map((dataRow: any[], dataIndex: number) => {
            const value = dataRow[index + 1];
            return {
              value: value,
              label: {
                show: itemBarWidth > 23,
                position: value >= 0 ? "top" : "bottom",
                color: "#666",
                fontSize: 12,
                formatter: function () {
                  return (
                    (props.yInverse
                      ? props.data?.[dataIndex + 1]?.[index + 1]
                      : value) + unit
                  );
                },
              },
            };
          });
        return {
          type: "bar",
          name: dim,
          barGap: 0,
          barCategoryGap: 30,
          barMaxWidth: 50,
          barMinWidth: 20,
          itemStyle: { color: constColorAll[index], backgroundColor: "red" },
          data: seriesData,
          label: {
            show: true,
            color: "#666",
            fontSize: 12,
            formatter: function (params: any) {
              return (
                (props.yInverse
                  ? props.data?.[params.dataIndex + 1]?.[params.seriesIndex + 1]
                  : params.value[params.seriesIndex + 1]) + unit
              );
            },
          },
        };
      });

    echart.value.setOption({ series: barSeries });

    echart.value.on("click", (params: any) => {
      if (params.seriesType === "bar") {
        currentIndex.value = params.dataIndex;
        let pixelPosition = echart.value!.convertToPixel(
          { xAxisIndex: 0 },
          params.name,
        );
        echart.value!.setOption({
          graphic: {
            id: "highlight-box",
            type: "rect",
            shape: {
              x: pixelPosition - singleLabelWidth / 2,
              y: gridRect.y,
              width: singleLabelWidth,
              height: gridRect.height - height,
            },
            style: { fill: params.color + "30" },
          },
        });
        let index = params.dataIndex;
        let newData = dataset?.source?.slice(1) ?? [];
        let name = newData?.[index]?.[0] ?? "";
        emit("HandleChartClick", index, name);
      }
    });

    if (dataset.source[1] && dataset.source[1].length > 1) {
      let defaultPixelPosition = echart.value.convertToPixel(
        { xAxisIndex: 0 },
        dataset.source[1][0],
      );
      echart.value.setOption({
        graphic: {
          id: "highlight-box",
          type: "rect",
          shape: {
            x: defaultPixelPosition - singleLabelWidth / 2,
            y: gridRect.y,
            width: singleLabelWidth,
            height: gridRect.height - height,
          },
          style: { fill: "rgba(84,112,198,0.1)" },
        },
      });
    }
  } else {
    const gridRect = echart.value
      .getModel()
      .getComponent("grid")
      .coordinateSystem.getRect();
    let singleLabelWidth = gridRect.width / dataxCount;
    let itemBarWidth = 20;
    if (isShowDataZoom.value) {
      singleLabelWidth = numSeries * 26 - 10;
    } else {
      itemBarWidth =
        (singleLabelWidth * 0.7) / numSeries > 50
          ? 50
          : (singleLabelWidth * 0.7) / numSeries;
    }

    const barSeries = (dataset.source[0] as string[])
      .slice(1)
      .map(function (dim, index) {
        const seriesData = dataset.source
          .slice(1)
          .map((dataRow: any[], dataIndex: number) => {
            const value = dataRow[index + 1];
            return {
              value: value,
              label: {
                show: itemBarWidth > 23,
                position: value >= 0 ? "top" : "bottom",
                color: "#666",
                fontSize: 12,
                formatter: function () {
                  return (
                    (props.yInverse
                      ? props.data?.[dataIndex + 1]?.[index + 1]
                      : value) + unit
                  );
                },
              },
            };
          });
        return {
          type: "bar",
          name: dim,
          barGap: 0,
          barCategoryGap: 30,
          barMaxWidth: 50,
          barMinWidth: 20,
          itemStyle: { color: constColorAll[index], backgroundColor: "red" },
          data: seriesData,
          label: {
            show: true,
            color: "#666",
            fontSize: 12,
            formatter: function (params: any) {
              return (
                (props.yInverse
                  ? props.data?.[params.dataIndex + 1]?.[params.seriesIndex + 1]
                  : params.value[params.seriesIndex + 1]) + unit
              );
            },
          },
        };
      });
    echart.value.setOption({ series: barSeries });
  }

  echart.value.on("datazoom", () => {
    const gridRect = echart
      .value!.getModel()
      .getComponent("grid")
      .coordinateSystem.getRect();
    let singleLabelWidth = gridRect.width / (props.data?.length || 1);
    let height = 0;
    if (isShowDataZoom.value) singleLabelWidth = singleSeriesWidth;

    if (
      currentIndex.value !== undefined &&
      dataset.source.length > currentIndex.value + 1
    ) {
      let pixelPosition = echart.value!.convertToPixel(
        { xAxisIndex: 0 },
        dataset.source[currentIndex.value + 1][0],
      );
      echart.value!.setOption({
        graphic: {
          id: "highlight-box",
          type: "rect",
          shape: {
            x: pixelPosition - singleLabelWidth / 2,
            y: gridRect.y,
            width: singleLabelWidth,
            height: gridRect.height - height,
          },
          style: { fill: "rgba(84,112,198,0.1)" },
        },
      });
    }
  });

  echart.value.on("legendselectchanged", handleLegendSelectChanged);
  window.addEventListener("resize", handleResize);
};

// ================= 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]);
    }
  }
  selectValue.value = "1";
  const legendData = Object.keys(selected).filter((item) => selected[item]);
  emit("ChangeChartOrder", selectValue.value, legendData, "");
  LoadEchart();
};

const handleCheckAllChange = (value: boolean | string | number) => {
  const isChecked = value === true || value === "true";
  if (isChecked) {
    legenSelectList.value = [...legenAllList.value];
    isIndeterminate.value = false;
  } else {
    legenSelectList.value = [];
    isIndeterminate.value = false;
  }
  LoadEchart();
};

const ChangeOrder = (val: string) => {
  selectValue.value = val;
  if (!props.data || !props.data[0]) return;
  const legendList = props.data[0].slice(1);
  const legendVal = legendList.map(
    (item: string) => legendSelected.value[item],
  );
  const selectedIndex = legendVal.indexOf(true);
  const legendData = Object.keys(legendSelected.value).filter(
    (item) => legendSelected.value[item],
  );
  emit("ChangeChartOrder", val, legendData, selectedIndex);
};

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

// ================= Watch =================
watch(
  () => props.data,
  () => {
    if (props.legendList && props.legendList.length > 0) {
      legenSelectList.value = props.legendList;
    }
    if (props.showSortSelectbox && props.data && props.data[0]) {
      const legenedList = props.data[0].slice(1);
      const barLegendList = legenedList.filter(
        (item: string) => legenSelectList.value.indexOf(item) > -1,
      );
      selectBarNum.value = barLegendList.length;
    }
    LoadEchart();
  },
  { deep: true },
);

// ================= Lifecycle =================
onMounted(() => {
  if (props.legendList && props.legendList.length > 0) {
    legenSelectList.value = props.legendList;
  }
  if (props.showSortSelectbox && props.data && props.data[0]) {
    const legenedList = props.data[0].slice(1);
    const barLegendList = legenedList.filter(
      (item: string) => props.legendList!.indexOf(item) > -1,
    );
    selectBarNum.value = barLegendList.length;
  }
  LoadEchart();
});

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

<style lang="scss" scoped>
.chart_width {
  width: 100%;
  height: 400px;
}
.title_right {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  position: absolute;
  top: 0px;
  right: 0;
  z-index: 99;
  .right_item {
    font-size: 14px;
    color: #999;
    font-weight: 400;
    cursor: pointer;
    height: 30px;
    line-height: 32px;
    margin-right: 5px;
    border-bottom: 2px solid #ffffff;
  }
  .item_active {
    font-size: 14px;
    color: #2e64fa;
    font-weight: 500;
    cursor: pointer;
  }
}
</style>
