<template>
  <div ref="chartRef" class="chart-host" />
</template>

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

type ChartOption = echarts.EChartsOption & {
  series?: Record<string, any> | Array<Record<string, any>>;
};

const props = withDefaults(
  defineProps<{
    chartOption?: ChartOption | null;
  }>(),
  {
    chartOption: null
  }
);

const chartRef = shallowRef<HTMLDivElement | null>(null);
const chart = shallowRef<echarts.ECharts | null>(null);
const resizeObserver = shallowRef<ResizeObserver | null>(null);
const pendingOption = shallowRef<ChartOption | null>(null);

const LIQUID_FILL_PLACEHOLDER_COLOR = 'rgba(19, 194, 194, 0.16)';

const clampPercent = (value: number) => {
  if (Number.isNaN(value)) {
    return 0;
  }
  return Math.min(100, Math.max(0, value));
};

// 当前项目未安装 echarts-liquidfill，这里降级成环形仪表盘，先保证报表稳定可用而不是整页报错。
const buildLiquidFillFallbackSeries = (series: Record<string, any>) => {
  const sourceData = Array.isArray(series?.data) ? series.data : [series?.data];
  const rawValue = Number(sourceData[0] ?? 0);
  const percentValue = clampPercent(rawValue * 100);
  const colors = Array.isArray(series?.color) && series.color.length ? series.color : ['#36cfc9'];

  return {
    type: 'gauge',
    min: 0,
    max: 100,
    startAngle: 90,
    endAngle: -270,
    radius: series?.radius || '74%',
    center: series?.center || ['50%', '50%'],
    pointer: {
      show: false
    },
    anchor: {
      show: false
    },
    progress: {
      show: true,
      roundCap: true,
      overlap: false,
      width: 18,
      itemStyle: {
        color: colors[0]
      }
    },
    axisLine: {
      lineStyle: {
        width: 18,
        color: [[1, LIQUID_FILL_PLACEHOLDER_COLOR]]
      }
    },
    splitLine: {
      show: false
    },
    axisTick: {
      show: false
    },
    axisLabel: {
      show: false
    },
    title: {
      show: false
    },
    detail: {
      valueAnimation: true,
      offsetCenter: [0, '0%'],
      color: series?.label?.color || colors[0],
      fontSize: series?.label?.fontSize || 20,
      formatter:
        typeof series?.label?.formatter === 'string'
          ? series.label.formatter
          : `${percentValue.toFixed(1)}%\n覆盖率`
    },
    data: [
      {
        value: percentValue
      }
    ]
  };
};

const normalizeOption = (option: ChartOption) => {
  const sourceSeries = (Array.isArray(option?.series) ? option.series : option?.series ? [option.series] : []) as Array<Record<string, any>>;
  if (!sourceSeries.some((item) => String(item?.type) === 'liquidFill')) {
    return option;
  }

  return {
    ...option,
    series: sourceSeries.map((item) => {
      if (String(item?.type) !== 'liquidFill') {
        return item;
      }
      return buildLiquidFillFallbackSeries(item);
    })
  } as ChartOption;
};

const initChart = () => {
  if (chart.value || !chartRef.value) {
    return chart.value;
  }
  // 统一只初始化一次实例，避免页面频繁切换条件渲染时重复创建导致内存抖动。
  chart.value = echarts.init(chartRef.value);
  return chart.value;
};

const resize = () => {
  chart.value?.resize();
};

const setData = async (option?: ChartOption | null) => {
  pendingOption.value = option || null;
  await nextTick();
  if (!pendingOption.value) {
    chart.value?.clear();
    return;
  }
  const instance = initChart();
  if (!instance) {
    return;
  }
  instance.clear();
  instance.setOption(normalizeOption(pendingOption.value), true);
  resize();
};

const destroyChart = () => {
  resizeObserver.value?.disconnect();
  resizeObserver.value = null;
  window.removeEventListener('resize', resize);
  chart.value?.dispose();
  chart.value = null;
};

const initResizeObserver = () => {
  window.addEventListener('resize', resize);
  if (!chartRef.value || typeof ResizeObserver === 'undefined') {
    return;
  }
  // 业务页大量使用抽屉、伸缩栏和栅格切换，仅监听 window 无法覆盖容器尺寸变化。
  resizeObserver.value = new ResizeObserver(() => {
    resize();
  });
  resizeObserver.value.observe(chartRef.value);
};

watch(
  () => props.chartOption,
  (option) => {
    void setData(option);
  },
  {
    deep: true
  }
);

onMounted(async () => {
  initResizeObserver();
  if (props.chartOption) {
    await setData(props.chartOption);
  }
});

onBeforeUnmount(() => {
  destroyChart();
});

defineExpose({
  chart,
  resize,
  setData
});
</script>

<style scoped>
.chart-host {
  width: 100%;
  height: 100%;
  min-width: 0;
}
</style>
