/**
 * ECharts-GL 三维环饼图（surface 参数方程）
 */

const MIN_SLICE_HEIGHT = 0.04
const MAX_SLICE_HEIGHT = 3.6

const getHeightScale = (value, maxValue) => {
  if (!maxValue || !value) {
    return MIN_SLICE_HEIGHT
  }
  const ratio = value / maxValue
  return MIN_SLICE_HEIGHT + (MAX_SLICE_HEIGHT - MIN_SLICE_HEIGHT) * ratio
}

export const getParametricEquation = (
  startRatio,
  endRatio,
  isSelected,
  isHovered,
  k,
  height
) => {
  const startRadian = startRatio * Math.PI * 2
  const endRadian = endRatio * Math.PI * 2
  const midRadian = (startRadian + endRadian) / 2
  const offsetX = isSelected ? Math.cos(midRadian) * 0.12 : 0
  const offsetY = isSelected ? Math.sin(midRadian) * 0.12 : 0
  const hoverRate = isHovered ? 1.06 : 1
  const sliceHeight = height * hoverRate

  return {
    u: {
      min: -Math.PI,
      max: Math.PI * 3,
      step: Math.PI / 32
    },
    v: {
      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) {
      if (u < -Math.PI * 0.5) {
        return Math.sin(u)
      }
      if (u > Math.PI * 2.5) {
        return Math.sin(u) * sliceHeight
      }
      return Math.sin(v) > 0 ? sliceHeight : -1
    }
  }
}

const PLACEHOLDER_SLICE_COUNT = 5
const PLACEHOLDER_SLICE_HEIGHT = 0.1

const DEFAULT_LABEL_LAYOUT = {
  centerX: 50,
  centerY: 40,
  projectScale: 33,
  yCompress: 0.66,
  startAngle: 0,
  radialLen: 9,
  horizontalLen: 11,
  lineColor: 'rgba(142, 207, 255, 0.55)',
  textColor: '#d8ecff',
  percentColor: '#ffffff',
  fontSize: 13
}

const buildOverlayLabelSeries = (pieData, overlayConfig = {}) => {
  const {
    center = ['38%', '41%'],
    radius = ['24%', '44%'],
    startAngle = 48,
    clockwise = false,
    nameColor = '#41a6fc',
    percentColor = '#ffffff',
    lineColor = 'rgba(65, 166, 252, 0.55)',
    fontSize = 13,
    labelLineLength = 10,
    labelLineLength2 = 12
  } = overlayConfig

  return {
    name: 'pie-label-overlay',
    type: 'pie',
    radius,
    center,
    startAngle,
    clockwise,
    silent: true,
    animation: false,
    z: 10,
    zlevel: 2,
    itemStyle: {
      color: 'transparent',
      borderWidth: 0,
      opacity: 0
    },
    emphasis: {
      disabled: true
    },
    labelLayout: {
      hideOverlap: false
    },
    labelLine: {
      show: true,
      length: labelLineLength,
      length2: labelLineLength2,
      smooth: false,
      lineStyle: {
        color: lineColor,
        width: 1
      }
    },
    label: {
      show: true,
      alignTo: 'edge',
      edgeDistance: 4,
      formatter: (params) => {
        const percent = params.data?.percent ?? params.percent ?? 0
        return `{name|${params.name}}\n{percent|${percent}%}`
      },
      rich: {
        name: {
          color: nameColor,
          fontSize,
          fontWeight: 500,
          lineHeight: 18,
          fontFamily: 'Microsoft YaHei'
        },
        percent: {
          color: percentColor,
          fontSize,
          fontWeight: 700,
          lineHeight: 18,
          fontFamily: 'Microsoft YaHei'
        }
      }
    },
    data: pieData.map((item) => ({
      name: item.name,
      value: item.value,
      percent: item.percent,
      labelLine: Number(item.percent) < 5
        ? {
            length: 6,
            length2: 22,
            lineStyle: { color: lineColor, width: 1 }
          }
        : undefined
    }))
  }
}

const projectSlicePoint = (midRatio, layout, view) => {
  const {
    centerX,
    centerY,
    projectScale,
    yCompress,
    startAngle
  } = layout
  const viewAlpha = view.viewAlpha ?? 28
  const viewBeta = view.viewBeta ?? 48

  const midRadian = startAngle + midRatio * Math.PI * 2
  const x = Math.cos(midRadian)
  const y = Math.sin(midRadian)

  const betaRad = (viewBeta * Math.PI) / 180
  const alphaRad = (viewAlpha * Math.PI) / 180

  const xb = x * Math.cos(betaRad) - y * Math.sin(betaRad)
  const yb = x * Math.sin(betaRad) + y * Math.cos(betaRad)
  const yf = yb * Math.cos(alphaRad)
  const xf = xb

  return {
    anchorX: centerX + xf * projectScale,
    anchorY: centerY - yf * projectScale * yCompress,
    dirX: xf,
    dirY: -yf
  }
}

const buildLabelGraphics = (sortedData, sumValue, labelOptions = {}) => {
  if (!sortedData.length || !sumValue) {
    return []
  }

  const layout = {
    ...DEFAULT_LABEL_LAYOUT,
    ...(labelOptions.layout || {})
  }
  const view = labelOptions.view || {}
  const textColor = labelOptions.textColor || layout.textColor
  const percentColor = labelOptions.percentColor || layout.percentColor
  const lineColor = labelOptions.lineColor || layout.lineColor
  const fontSize = labelOptions.fontSize || layout.fontSize

  let startValue = 0
  const graphics = []

  sortedData.forEach((item) => {
    const endValue = startValue + item.value
    const midRatio = (startValue + endValue) / 2 / sumValue
    const sliceRatio = (endValue - startValue) / sumValue
    const { anchorX, anchorY, dirX, dirY } = projectSlicePoint(midRatio, layout, view)
    const onRight = dirX >= 0
    const dirLen = Math.hypot(dirX, dirY) || 1
    const normX = dirX / dirLen
    const normY = dirY / dirLen
    const radialBoost = sliceRatio < 0.05 ? 6 : 0
    const horizontalBoost = sliceRatio < 0.05 ? 8 : 0
    const { radialLen, horizontalLen } = layout

    const radialX = normX * (radialLen + radialBoost)
    const radialY = normY * (radialLen + radialBoost)
    const endX = radialX + (onRight ? horizontalLen + horizontalBoost : -(horizontalLen + horizontalBoost))
    const endY = radialY

    graphics.push({
      type: 'group',
      left: `${anchorX}%`,
      top: `${anchorY}%`,
      z: 10,
      children: [
        {
          type: 'polyline',
          shape: {
            points: [
              [0, 0],
              [radialX, radialY],
              [endX, endY]
            ]
          },
          style: {
            stroke: lineColor,
            lineWidth: 1,
            fill: null
          },
          silent: true
        },
        {
          type: 'group',
          x: endX + (onRight ? 6 : -6),
          y: endY,
          children: [
            {
              type: 'text',
              style: {
                text: item.name,
                fill: textColor,
                font: `${fontSize}px Microsoft YaHei`,
                textAlign: onRight ? 'left' : 'right',
                textVerticalAlign: 'bottom'
              },
              silent: true
            },
            {
              type: 'text',
              y: 16,
              style: {
                text: `${item.percent}%`,
                fill: percentColor,
                font: `700 ${fontSize}px Microsoft YaHei`,
                textAlign: onRight ? 'left' : 'right',
                textVerticalAlign: 'top'
              },
              silent: true
            }
          ],
          silent: true
        }
      ],
      silent: true
    })

    startValue = endValue
  })

  return graphics
}

const buildCenterGraphic = (text) => ([
  {
    type: 'text',
    left: 'center',
    top: '44%',
    style: {
      text,
      fill: 'rgba(142, 207, 255, 0.88)',
      font: '14px Microsoft YaHei',
      textAlign: 'center',
      textVerticalAlign: 'middle'
    },
    silent: true
  }
])

export const buildEmptyPie3DPlaceholder = (colors, config = {}) => {
  const pieData = Array.from({ length: PLACEHOLDER_SLICE_COUNT }, (_, index) => ({
    name: `empty-${index}`,
    value: 1,
    percent: 20,
    color: colors[index % colors.length],
    opacity: 0.42
  }))

  return buildPie3DOption({
    ...config,
    pieData,
    placeholderMode: true
  })
}

export const buildPie3DOption = ({
  pieData,
  internalDiameterRatio = 0.58,
  boxHeight = 40,
  viewAlpha = 28,
  viewBeta = 48,
  viewDistance = 185,
  placeholderMode = false,
  preserveOrder = false,
  labelMode = 'graphic',
  autoRotate = false,
  autoRotateSpeed = 6,
  labelColor,
  labelPercentColor,
  labelLayout,
  labelLineColor,
  labelOverlay
}) => {
  const series = []
  let sumValue = 0
  let startValue = 0

  const validData = placeholderMode
    ? pieData
    : pieData.filter((item) => (Number(item.value) || 0) > 0)

  if (!validData.length) {
    return null
  }

  const sortedData = placeholderMode || preserveOrder
    ? validData
    : [...validData].sort((a, b) => b.value - a.value)

  const maxValue = placeholderMode
    ? 1
    : Math.max(...sortedData.map((item) => item.value), 1)
  const k = (1 - internalDiameterRatio) / (1 + internalDiameterRatio)

  sortedData.forEach((item) => {
    sumValue += item.value
  })

  sortedData.forEach((item) => {
    const endValue = startValue + item.value
    const startRatio = startValue / sumValue
    const endRatio = endValue / sumValue
    const height = placeholderMode
      ? PLACEHOLDER_SLICE_HEIGHT
      : getHeightScale(item.value, maxValue)

    series.push({
      name: item.name,
      type: 'surface',
      coordinateSystem: 'cartesian3D',
      parametric: true,
      progressive: 0,
      wireframe: { show: false },
      shading: 'lambert',
      pieData: item,
      itemStyle: {
        color: item.color,
        opacity: item.opacity ?? (placeholderMode ? 0.42 : 0.96)
      },
      parametricEquation: getParametricEquation(
        startRatio,
        endRatio,
        false,
        false,
        k,
        height
      )
    })

    startValue = endValue
  })

  return {
    animation: false,
    tooltip: placeholderMode
      ? { show: false }
      : {
          trigger: 'item',
          formatter: (params) => {
            if (!params.seriesName) {
              return ''
            }
            const target = series.find((item) => item.name === params.seriesName)
            const data = target?.pieData
            if (!data) {
              return params.seriesName
            }
            return `${data.name}<br/>${data.percent}%　${data.value}头`
          }
        },
    xAxis3D: { min: -1.2, max: 1.2 },
    yAxis3D: { min: -1.2, max: 1.2 },
    zAxis3D: { min: -2, max: 4.5 },
    grid3D: {
      show: false,
      boxHeight,
      top: '-12%',
      viewControl: {
        alpha: viewAlpha,
        beta: viewBeta,
        distance: viewDistance,
        rotateSensitivity: 0,
        zoomSensitivity: 0,
        panSensitivity: 0,
        autoRotate,
        autoRotateSpeed
      },
      light: {
        main: {
          intensity: 1.2,
          shadow: false,
          alpha: 40,
          beta: 40
        },
        ambient: {
          intensity: 0.5
        }
      }
    },
    series: [
      ...series,
      ...(labelMode === 'overlay' && !placeholderMode
        ? [buildOverlayLabelSeries(sortedData, {
            nameColor: labelColor,
            percentColor: labelPercentColor,
            lineColor: labelLineColor,
            ...(labelOverlay || {})
          })]
        : [])
    ],
    graphic: placeholderMode
      ? buildCenterGraphic('暂无统计数据')
      : labelMode === 'none' || labelMode === 'overlay'
        ? []
        : buildLabelGraphics(sortedData, sumValue, {
            textColor: labelColor,
            percentColor: labelPercentColor,
            lineColor: labelLineColor,
            layout: labelLayout,
            view: { viewAlpha, viewBeta, viewDistance }
          })
  }
}

export const enrichPieData = (items, colors) => {
  const total = items.reduce((sum, item) => sum + (Number(item.value) || 0), 0)
  return items.map((item, index) => {
    const value = Number(item.value) || 0
    const percent = total === 0 ? 0 : Number(((value / total) * 100).toFixed(2))
    return {
      name: item.name,
      value,
      percent,
      color: colors[index % colors.length]
    }
  })
}
