import {
  G2PlotChartView,
  G2PlotDrawOptions
} from '@/views/chart/components/js/panel/types/impl/g2plot'
import type { Pie as G2Pie, PieOptions } from '@antv/g2plot/esm/plots/pie'
import {
  flow,
  hexColorToRGBA,
  parseJson,
  setUpSingleDimensionSeriesColor
} from '@/views/chart/components/js/util'
import {
  configPlotTooltipEvent,
  getPadding,
  getTooltipContainer,
  getTooltipSeriesTotalMap,
  TOOLTIP_TPL
} from '@/views/chart/components/js/panel/common/common_antv'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import {
  PIE_AXIS_CONFIG,
  PIE_AXIS_TYPE,
  PIE_EDITOR_PROPERTY,
  PIE_EDITOR_PROPERTY_INNER
} from '@/views/chart/components/js/panel/charts/pie/common'
import type { Datum } from '@antv/g2plot/esm/types/common'
import { add } from 'mathjs'
import isEmpty from 'lodash-es/isEmpty'
import { cloneDeep } from 'lodash-es'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
const DEFAULT_DATA = []
export class Pie extends G2PlotChartView<PieOptions, G2Pie> {
  axis: AxisType[] = PIE_AXIS_TYPE
  properties = PIE_EDITOR_PROPERTY
  propertyInner: EditorPropertyInner = {
    ...PIE_EDITOR_PROPERTY_INNER,
    'basic-style-selector': ['colors', 'alpha', 'radius', 'topN', 'seriesColor'],
    'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel']
  }
  axisConfig = PIE_AXIS_CONFIG

  async drawChart(drawOptions: G2PlotDrawOptions<G2Pie>): Promise<G2Pie> {
    const { chart, container, action } = drawOptions
    this.configEmptyDataStyle(chart.data?.data, container, null, t('chart.no_data_or_not_positive'))
    chart.container = container
    if (!chart.data?.data?.length) {
      return
    }
    // data
    const data = chart.data.data
    // custom color
    const customAttr = parseJson(chart.customAttr)
    const color = customAttr.basicStyle.colors.map(i =>
      hexColorToRGBA(i, customAttr.basicStyle.alpha)
    )
    // options
    const initOptions: PieOptions = {
      data: data,
      angleField: 'value',
      colorField: 'field',
      appendPadding: getPadding(chart),
      color,
      animation: false,
      pieStyle: {
        lineWidth: 0
      },
      statistic: {
        title: false,
        content: {
          style: {
            whiteSpace: 'pre-wrap',
            overflow: 'hidden',
            textOverflow: 'ellipsis'
          },
          content: ''
        }
      },
      interactions: [
        {
          type: 'legend-active',
          cfg: {
            start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }],
            end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }]
          }
        },
        {
          type: 'legend-filter',
          cfg: {
            start: [
              {
                trigger: 'legend-item:click',
                action: [
                  'list-unchecked:toggle',
                  'data-filter:filter',
                  'element-active:reset',
                  'element-highlight:reset'
                ]
              }
            ]
          }
        },
        {
          type: 'tooltip',
          cfg: {
            start: [{ trigger: 'interval:mousemove', action: 'tooltip:show' }],
            end: [{ trigger: 'interval:mouseleave', action: 'tooltip:hide' }]
          }
        },
        {
          type: 'active-region',
          cfg: {
            start: [{ trigger: 'interval:mousemove', action: 'active-region:show' }],
            end: [{ trigger: 'interval:mouseleave', action: 'active-region:hide' }]
          }
        }
      ],
      meta: {
        field: {
          type: 'cat'
        }
      },
      state: {
        active: {
          style: {
            lineWidth: 2,
            fillOpacity: 0.5
          }
        }
      }
    }
    const options = this.setupOptions(chart, initOptions)
    const { Pie: G2Pie } = await import('@antv/g2plot/esm/plots/pie')
    const newChart = new G2Pie(container, options)
    newChart.on('interval:click', d => {
      if (customAttr.basicStyle.calcTopN && d.data?.data?.others) {
        return
      }
      action(d)
    })
    configPlotTooltipEvent(chart, newChart)
    return newChart
  }

  protected configLabel(chart: Chart, options: PieOptions): PieOptions {
    const { label: labelAttr } = parseJson(chart.customAttr)
    if (!labelAttr?.show) {
      return {
        ...options,
        label: false
      }
    }
    const layout = []
    let textAlign = undefined
    if (labelAttr.position === 'inner') {
      textAlign = 'center'
      if (labelAttr.fullDisplay) {
        layout.push({ type: 'limit-in-plot' })
      } else {
        layout.push({ type: 'limit-in-canvas' })
        layout.push({ type: 'hide-overlap' })
      }
    } else {
      if (!labelAttr.fullDisplay) {
        layout.push({ type: 'limit-in-plot' })
      }
    }
    let labelType = labelAttr.position === 'outer' ? 'spider' : labelAttr.position
    if (layout.length === 0) {
      labelType = 'no'
    }
    const label = {
      type: labelType,
      textAlign,
      layout,
      autoRotate: false,
      style: {
        fill: labelAttr.color,
        fontSize: labelAttr.fontSize
      },
      formatter: (param: Datum) => {
        let res = param.value
        const contentItems = []
        if (labelAttr.showDimension) {
          contentItems.push(param.field)
        }
        if (labelAttr.showQuota) {
          contentItems.push(valueFormatter(param.value, labelAttr.quotaLabelFormatter))
        }
        if (labelAttr.showProportion) {
          const percentage = `${(Math.round(param.percent * 10000) / 100).toFixed(
            labelAttr.reserveDecimalCount
          )}%`
          if (labelAttr.showDimension && labelAttr.showQuota) {
            contentItems.push(`(${percentage})`)
          } else {
            contentItems.push(percentage)
          }
        }
        res = contentItems.join(' ')
        return res
      }
    }
    return { ...options, label }
  }

  protected configTooltip(chart: Chart, options: PieOptions): PieOptions {
    const { tooltip: tooltipAttr, label } = parseJson(chart.customAttr)
    const { yAxis } = chart
    if (!tooltipAttr.show) {
      return {
        ...options,
        tooltip: false
      }
    }
    const reserveDecimalCount = label.reserveDecimalCount
    const seriesTotalMap = getTooltipSeriesTotalMap(options.data)
    // trick, cal total, maybe use scale of chart in plot instance
    const total = options.data?.reduce((pre, next) => add(pre, next.value ?? 0), 0)
    const formatterMap = tooltipAttr.seriesTooltipFormatter
      ?.filter(i => i.show)
      .reduce((pre, next) => {
        pre[next.id] = next
        return pre
      }, {}) as Record<string, SeriesFormatter>
    const tooltip: PieOptions['tooltip'] = {
      showTitle: true,
      title: () => undefined,
      customItems(originalItems) {
        let tooltipItems = originalItems
        if (tooltipAttr.seriesTooltipFormatter?.length) {
          tooltipItems = originalItems.filter(item => formatterMap[item.data.quotaList[0].id])
        }
        const result = []
        const head = originalItems[0]
        tooltipItems.forEach(item => {
          const formatter = formatterMap[item.data.quotaList[0].id] ?? yAxis[0]
          const originValue = parseFloat(item.value as string)
          const value = valueFormatter(originValue, formatter.formatterCfg)
          // sync with label
          const percent = (Math.round((originValue / total) * 10000) / 100).toFixed(
            reserveDecimalCount
          )
          const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName
          result.push({ ...item, name, value: `${value ?? ''} (${percent}%)` })
        })
        head.data.dynamicTooltipValue?.forEach(item => {
          const formatter = formatterMap[item.fieldId]
          if (formatter) {
            const total = seriesTotalMap[item.fieldId]
            // sync with label
            const percent = (Math.round((item.value / total) * 10000) / 100).toFixed(
              reserveDecimalCount
            )
            const value = valueFormatter(parseFloat(item.value), formatter.formatterCfg)
            const name = isEmpty(formatter.chartShowName) ? formatter.name : formatter.chartShowName
            result.push({ color: 'grey', name, value: `${value ?? ''} (${percent}%)` })
          }
        })
        return result
      },
      container: getTooltipContainer(`tooltip-${chart.id}`),
      itemTpl: TOOLTIP_TPL,
      shared: true,
      enterable: true
    }
    return {
      ...options,
      tooltip
    }
  }

  protected configBasicStyle(chart: Chart, options: PieOptions): PieOptions {
    const customAttr = parseJson(chart.customAttr)
    const { basicStyle } = customAttr
    const { data } = options
    if (data?.length && basicStyle.calcTopN && data.length > basicStyle.topN) {
      data.sort((a, b) => b.value - a.value)
      const otherItems = data.splice(basicStyle.topN)
      const initOtherItem = {
        ...data[0],
        dynamicTooltipValue: [],
        field: basicStyle.topNLabel,
        name: basicStyle.topNLabel,
        value: 0,
        others: true
      }
      const dynamicTotalMap: Record<string, number> = {}
      otherItems.reduce((p, n) => {
        p.value += n.value ?? 0
        n.dynamicTooltipValue?.forEach(val => {
          dynamicTotalMap[val.fieldId] = (dynamicTotalMap[val.fieldId] || 0) + val.value
        })
        return p
      }, initOtherItem)
      for (const key in dynamicTotalMap) {
        initOtherItem.dynamicTooltipValue.push({
          fieldId: key,
          value: dynamicTotalMap[key]
        })
      }
      data.push(initOtherItem)
    }
    return {
      ...options,
      radius: basicStyle.radius / 100
    }
  }
  setupDefaultOptions(chart: ChartObj): ChartObj {
    const { customAttr, customStyle } = chart
    const { label } = customAttr
    if (!['inner', 'outer'].includes(label.position)) {
      label.position = 'outer'
    }
    customAttr.label = {
      ...label,
      show: true,
      showDimension: true,
      showProportion: true,
      reserveDecimalCount: 2
    }
    const { legend } = customStyle
    legend.show = false
    return chart
  }

  public setupSeriesColor(chart: ChartObj, data?: any[]): ChartBasicStyle['seriesColor'] {
    data = cloneDeep(data)
    const { calcTopN, topN, topNLabel } = chart.customAttr.basicStyle
    if (data?.length && calcTopN && data.length > topN) {
      data.sort((a, b) => b.value - a.value)
      data.splice(topN)
      data.push({
        field: topNLabel,
        value: 0
      })
    }
    return setUpSingleDimensionSeriesColor(chart, data)
  }

  protected setupOptions(chart: Chart, options: PieOptions): PieOptions {
    return flow(
      this.configTheme,
      this.configBasicStyle,
      this.configSingleDimensionColor,
      this.configLabel,
      this.configTooltip,
      this.configLegend
    )(chart, options, {}, this)
  }

  constructor(name = 'pie') {
    super(name, DEFAULT_DATA)
  }
}

export class PieDonut extends Pie {
  propertyInner: EditorPropertyInner = {
    ...PIE_EDITOR_PROPERTY_INNER,
    'basic-style-selector': ['colors', 'alpha', 'radius', 'innerRadius', 'topN', 'seriesColor'],
    'tooltip-selector': [...PIE_EDITOR_PROPERTY_INNER['tooltip-selector'], 'carousel']
  }
  protected configBasicStyle(chart: Chart, options: PieOptions): PieOptions {
    const tmp = super.configBasicStyle(chart, options)
    const { basicStyle } = parseJson(chart.customAttr)
    return {
      ...tmp,
      radius: basicStyle.radius / 100,
      innerRadius: basicStyle.innerRadius / 100
    }
  }

  constructor() {
    super('pie-donut')
  }
}
