/**
 * Copyright (c) 2022 - present TinyVue Authors.
 * Copyright (c) 2022 - present Huawei Cloud Computing Technologies Co., Ltd.
 *
 * Use of this source code is governed by an MIT-style license.
 *
 * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
 * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
 * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
 *
 */

import { getFormated, hexToRgb } from '../chart-core/deps/utils'
import { itemPoint, itemLabel, itemContent } from '../chart-core/deps/constants'
import { line } from '../chart-line'
import { isNull } from '@opentiny/utils'

const getScatterLegend = (dataLabels, legendName) => {
  function formatter(name) {
    return isNull(legendName[name]) ? name : legendName[name]
  }

  return { data: dataLabels, formatter, itemStyle: { borderWidth: 1 } }
}

const getTooltipContent = (item, args) => {
  const { labelMap, columns } = args
  const { dataType, digit } = args
  const template = []
  const { color, seriesName, data } = item
  const { value } = data

  template.push(`${itemPoint(color)} ${seriesName}<br>`)

  value.forEach((d, i) => {
    const name = labelMap[columns[i]] || columns[i]
    const num = isNaN(d) ? d : getFormated(d, dataType[columns[i]], digit)

    template.push(`${itemLabel(name)}${itemContent(num)}<br>`)
  })

  return template.join('')
}

const getScatterTooltip = (args) => {
  const { tooltipTrigger } = args

  function formatter(item) {
    return Array.isArray(item) ? item.map((i) => getTooltipContent(i, args)).join('') : getTooltipContent(item, args)
  }

  return { trigger: tooltipTrigger, formatter }
}

const getScatterXAxis = (args) => {
  const { rows, dataLabels, dimension } = args
  const { xAxisName, axisVisible, xAxisType } = args
  const data = []

  dataLabels.forEach((dataLabel) => {
    const items = rows[dataLabel]

    items.forEach((item) => {
      const name = item[dimension]

      name && !~data.indexOf(name) && data.push(name)
    })
  })

  return [
    {
      type: xAxisType,
      show: axisVisible,
      name: xAxisName,
      data,
      offset: 5,
      axisLabel: { show: true, color: '#4E4E4E' }
    }
  ]
}

const getScatterYAxis = (args) => {
  const { axisVisible, dataType, digit, min } = args
  const { metrics, max, scale, yAxisName } = args

  function formatter(val) {
    return getFormated(val, dataType[metrics[0]], digit)
  }

  let result = { type: 'value', show: axisVisible, scale, min, max }

  Object.assign(result, {
    axisTick: { show: false },
    name: yAxisName,
    offset: 10,
    axisLabel: { show: true, formatter, color: '#4E4E4E' },
    splitLine: { show: true, lineStyle: { color: 'rgba(25,25,25,0.10)' } }
  })

  return result
}

const getScatterSeries = (args) => {
  const { columns, cursor, dataLabels, dimension, itemStyle, label, metrics, color } = args
  const { rows, symbol, symbolRotate, symbolSize, symbolSizeMax, symbolOffset } = args
  const extraMetrics = columns.filter((column) => !~metrics.indexOf(column) && column !== dimension)
  const numbers = []

  dataLabels.forEach((dataLabel) => rows[dataLabel].forEach((row) => numbers.push(row[metrics[1]])))

  const { maxNum = Math.max(...numbers), series = [] } = {}

  dataLabels.forEach((dataLabel, index) => {
    const { result = [], itemData = rows[dataLabel] } = {}

    itemData.forEach((item) => {
      const itemResult = { value: [] }

      itemResult.value.push(item[dimension], item[metrics[0]], item[metrics[1]])

      extraMetrics.forEach((ext) => itemResult.value.push(item[ext]))

      itemResult.symbolSize = symbolSize || (item[metrics[1]] / maxNum) * symbolSizeMax

      result.push(itemResult)
    })

    const serie = {
      type: 'scatter',
      data: result,
      name: dataLabel,
      label,
      itemStyle: {
        borderWidth: 1,
        borderColor: color[index],
        color: `rgba(${hexToRgb(color[index])}, 0.3)`,
        ...(itemStyle || {})
      }
    }

    Object.assign(serie, { symbol, symbolRotate, symbolOffset, cursor })

    series.push(serie)
  })

  return series
}

const getSeries = (args) => {
  const { cursor, label, tooltip, itemStyle, symbolSizeMax } = args
  const { series, color, symbol, symbolSize, symbolRotate, symbolOffset } = args
  series.forEach((item, index) => {
    const itemBase = { type: 'scatter', symbol }
    const numbers = []
    const data = item.data

    data.forEach((row) => numbers.push(row[1]))

    const maxNum = Math.max(...numbers)

    item.data = data.map((item) => ({
      value: item,
      symbolSize: symbolSize || (item[1] / maxNum) * symbolSizeMax
    }))

    Object.assign(itemBase, {
      symbolRotate,
      symbolOffset,
      cursor,
      label,
      tooltip,
      itemStyle: {
        borderWidth: 1,
        borderColor: color[index],
        color: `rgba(${hexToRgb(color[index])}, 0.3)`,
        ...(itemStyle || {})
      },
      emphasis: { itemStyle: { borderWidth: 1, borderColor: color[index] } }
    })
    Object.assign(item, itemBase)
  })
  return series
}

export const scatter = (columns, rows, settings, extra) => {
  const { dimension = columns[0], metrics = [columns[1], columns[2]], dataType = {} } = settings
  const { xAxisType = 'category', xAxisName, yAxisName, digit = 2, legendName = {} } = settings
  const { labelMap = {}, tooltipTrigger = 'item', axisVisible = true, symbolSizeMax = 50, symbol } = settings
  const { symbolSize, symbolRotate, symbolOffset, cursor, min, max, scale, label, itemStyle } = settings
  const { tooltipVisible, legendVisible, color } = extra
  const tooltip = tooltipVisible && getScatterTooltip({ tooltipTrigger, labelMap, columns, dataType, digit })

  if (Array.isArray(rows)) {
    const lineSettings = {
      ...settings,
      ...{
        yAxisName: yAxisName ? [yAxisName] : undefined,
        xAxisName: xAxisName ? [xAxisName] : undefined,
        dimension: dimension ? [dimension] : undefined,
        max: max ? [max] : undefined,
        min: min ? [min] : undefined,
        scale: scale ? [scale] : undefined
      }
    }

    const options = line(columns, rows, lineSettings, extra)

    if (!options || !options.series) {
      return {}
    }

    const baseObj = { color, symbol, symbolSize, symbolRotate, symbolOffset, cursor, label, itemStyle, symbolSizeMax }
    options.series = getSeries({ tooltip, series: options.series, ...baseObj })
    options.legend = { itemStyle: { borderWidth: 1 }, ...(options.legend || {}) }

    return options
  }

  const dataLabels = Object.keys(rows)
  const legend = legendVisible && getScatterLegend(dataLabels, legendName)
  const xAxisParam = { xAxisName, axisVisible, xAxisType, dataLabels, dimension, rows }
  const xAxis = getScatterXAxis(xAxisParam)
  const yAxisParam = { min, max, scale, yAxisName, dataType, metrics, digit, axisVisible }
  const yAxis = getScatterYAxis(yAxisParam)
  const seriesParam = { rows, dataLabels, columns, metrics, dimension, label, itemStyle, symbol }

  Object.assign(seriesParam, { symbolSizeMax, symbolSize, symbolRotate, symbolOffset, cursor, color })

  const series = getScatterSeries(seriesParam)

  return { legend, tooltip, xAxis, yAxis, series }
}