import {
  type LayoutResult,
  S2DataConfig,
  S2Event,
  S2Options,
  S2Theme,
  ScrollbarPositionType,
  TableColCell,
  TableSheet,
  ViewMeta
} from '@antv/s2'
import { formatterItem, valueFormatter } from '../../../formatter'
import { hexColorToRGBA, isAlphaColor, parseJson } from '../../../util'
import { S2ChartView, S2DrawOptions } from '../../types/impl/s2'
import { TABLE_EDITOR_PROPERTY, TABLE_EDITOR_PROPERTY_INNER } from './common'
import { useI18n } from '@/hooks/web/useI18n'
import { filter, isEqual, merge } from 'lodash-es'
import {
  calcTreeWidth,
  calculateGroupHeaderHeight,
  calculateHeaderHeight,
  configEmptyDataStyle,
  copyContent,
  CustomDataCell,
  CustomTableColCell,
  drawImage,
  getColumns,
  getLeafNodes,
  getRowIndex,
  getStartPosition,
  getSummaryRow,
  isNumeric,
  SortTooltip,
  SummaryCell,
  summaryRowStyle
} from '@/views/chart/components/js/panel/common/common_table'

const { t } = useI18n()

class ImageCell extends CustomDataCell {
  protected drawTextShape(): void {
    drawImage.apply(this)
  }
}
/**
 * 明细表
 */
export class TableInfo extends S2ChartView<TableSheet> {
  properties = TABLE_EDITOR_PROPERTY
  propertyInner = {
    ...TABLE_EDITOR_PROPERTY_INNER,
    'table-header-selector': [
      ...TABLE_EDITOR_PROPERTY_INNER['table-header-selector'],
      'tableHeaderSort',
      'showTableHeader',
      'headerGroup'
    ],
    'basic-style-selector': [
      'tableColumnMode',
      'tableBorderColor',
      'tableScrollBarColor',
      'alpha',
      'tablePageMode',
      'showHoverStyle',
      'autoWrap'
    ],
    'table-cell-selector': [
      ...TABLE_EDITOR_PROPERTY_INNER['table-cell-selector'],
      'tableFreeze',
      'tableColumnFreezeHead',
      'tableRowFreezeHead',
      'mergeCells'
    ],
    'summary-selector': ['showSummary', 'summaryLabel']
  }
  axis: AxisType[] = ['xAxis', 'filter', 'drill']
  axisConfig: AxisConfig = {
    xAxis: {
      name: `${t('chart.drag_block_table_data_column')} / ${t('chart.dimension_or_quota')}`
    }
  }

  public drawChart(drawOption: S2DrawOptions<TableSheet>): TableSheet {
    const { container, chart, pageInfo, action, resizeAction } = drawOption
    const containerDom = document.getElementById(container)

    // fields
    let fields = chart.data?.fields ?? []
    const columns = []
    const meta = []
    const axisMap = chart.xAxis.reduce((pre, cur) => {
      pre[cur.dataeaseName] = cur
      return pre
    }, {})
    const drillFieldMap = {}
    if (chart.drill) {
      // 下钻过滤字段
      const filterFields = chart.drillFilters.map(i => i.fieldId)
      // 下钻入口的字段下标
      const drillFieldId = chart.drillFields[0].id
      const drillFieldIndex = chart.xAxis.findIndex(ele => ele.id === drillFieldId)
      // 当前下钻字段
      const curDrillFieldId = chart.drillFields[filterFields.length].id
      const curDrillField = fields.find(ele => ele.id === curDrillFieldId)
      filterFields.push(curDrillFieldId)
      // 移除下钻字段，把当前下钻字段插入到下钻入口位置
      fields = fields.filter(ele => {
        return !filterFields.includes(ele.id)
      })
      drillFieldMap[curDrillField.dataeaseName] = chart.drillFields[0].dataeaseName
      fields.splice(drillFieldIndex, 0, curDrillField)
    }
    fields.forEach(ele => {
      const f = axisMap[ele.dataeaseName]
      if (f?.hide === true) {
        return
      }
      columns.push(ele.dataeaseName)
      meta.push({
        field: ele.dataeaseName,
        name: ele.chartShowName ?? ele.name,
        formatter: function (value) {
          if (!f) {
            return value
          }
          if (value === null || value === undefined) {
            return value
          }
          if (![2, 3, 4].includes(f.deType) || !isNumeric(value)) {
            return value
          }
          let formatCfg = f.formatterCfg
          if (!formatCfg) {
            formatCfg = formatterItem
          }
          return valueFormatter(value, formatCfg)
        },
        id: ele.id
      })
    })
    const { basicStyle, tableCell, tableHeader, tooltip } = parseJson(chart.customAttr)
    // 表头分组
    const { headerGroup, showTableHeader } = tableHeader
    if (headerGroup && showTableHeader !== false) {
      const { headerGroupConfig } = tableHeader
      if (headerGroupConfig?.columns?.length) {
        const allKeys = columns.map(c => drillFieldMap[c] || c)
        const leafNodes = getLeafNodes(headerGroupConfig.columns as ColumnNode[])
        const leafKeys = leafNodes.map(c => c.key)
        if (isEqual(leafKeys, allKeys)) {
          if (Object.keys(drillFieldMap).length) {
            const originField = Object.values(drillFieldMap)[0]
            const drillField = Object.keys(drillFieldMap)[0]
            const [drillCol] = getColumns([originField], headerGroupConfig.columns as ColumnNode[])
            drillCol.key = drillField
          }
          columns.splice(0, columns.length, ...headerGroupConfig.columns)
          meta.push(...headerGroupConfig.meta)
        }
      }
    }
    // 空值处理
    const newData = this.configEmptyDataStrategy(chart)
    // data config
    const s2DataConfig: S2DataConfig = {
      fields: {
        columns: columns
      },
      meta: meta,
      data: newData
    }

    // options
    const s2Options: S2Options = {
      width: containerDom.getBoundingClientRect().width,
      height: containerDom.offsetHeight,
      showSeriesNumber: tableHeader.showIndex,
      conditions: this.configConditions(chart),
      tooltip: {
        getContainer: () => containerDom,
        renderTooltip: sheet => new SortTooltip(sheet)
      },
      interaction: {
        hoverHighlight: !(basicStyle.showHoverStyle === false),
        scrollbarPosition: newData.length
          ? ScrollbarPositionType.CONTENT
          : ScrollbarPositionType.CANVAS,
        hoverFocus: false
      }
    }
    s2Options.style = this.configStyle(chart, s2DataConfig)
    // 自适应列宽模式下，URL 字段的宽度固定为 120
    if (basicStyle.tableColumnMode === 'adapt') {
      const urlFields = fields.filter(
        field => field.deType === 7 && !axisMap[field.dataeaseName]?.hide
      )
      s2Options.style.colCfg.widthByFieldValue = urlFields?.reduce((p, n) => {
        p[n.chartShowName ?? n.name] = 120
        return p
      }, {})
    }
    if (tableCell.tableFreeze && !tableCell.mergeCells) {
      s2Options.frozenColCount = tableCell.tableColumnFreezeHead ?? 0
      s2Options.frozenRowCount = tableCell.tableRowFreezeHead ?? 0
    }
    // tooltip
    this.configTooltip(chart, s2Options)
    // 合并单元格
    this.configMergeCells(chart, s2Options, s2DataConfig)
    // 隐藏表头，保留顶部的分割线, 禁用表头横向 resize
    if (tableHeader.showTableHeader === false) {
      s2Options.style.colCfg.height = 1
      if (tableCell.showHorizonBorder === false) {
        s2Options.style.colCfg.height = 0
      }
      s2Options.interaction.resize = {
        colCellVertical: false
      }
      s2Options.colCell = (node, sheet, config) => {
        node.label = ' '
        return new TableColCell(node, sheet, config)
      }
    } else {
      // header interaction
      chart.container = container
      this.configHeaderInteraction(chart, s2Options)
      s2Options.colCell = (node, sheet, config) => {
        // 配置文本自动换行参数
        node.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap
        node.maxLines = basicStyle.maxLines
        return new CustomTableColCell(node, sheet, config)
      }
    }
    // 序列号和总计行
    this.configSummaryRowAndIndex(chart, pageInfo, s2Options, s2DataConfig)
    // 开始渲染
    const newChart = new TableSheet(containerDom, s2DataConfig, s2Options)
    // 总计紧贴在单元格后面
    summaryRowStyle(newChart, newData, tableCell, tableHeader, basicStyle.showSummary)
    // 开启自动换行
    if (basicStyle.autoWrap && !tableCell.mergeCells) {
      // 记录调整列宽的信息
      const setResizeColWidthInfo = (info?) => {
        newChart.store.set('resizeColWidthInfo', info ? info : undefined)
      }
      setResizeColWidthInfo()
      // 计算分组表头的高度
      newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => {
        calculateGroupHeaderHeight(newChart, tableHeader, basicStyle)
        setResizeColWidthInfo()
      })
      // 调整行高不能小于初始行高
      newChart.on(S2Event.LAYOUT_RESIZE_COL_HEIGHT, info => {
        if (info.info.resizedHeight < newChart.options.style.colCfg.height) {
          info.style.colCfg.heightByField[info.info.id] = newChart.options.style.colCfg.height
        }
      })
      // 调整表头单元格宽度时，计算表头高度
      newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, info => {
        setResizeColWidthInfo(info.info)
        calculateHeaderHeight(info, newChart, tableHeader, basicStyle, null)
      })
      newChart.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev: LayoutResult) => {
        if (ev.colLeafNodes?.length) {
          const { value, width } = ev.colLeafNodes[0]
          calculateHeaderHeight(
            { info: { meta: { value }, resizedWidth: width } },
            newChart,
            tableHeader,
            basicStyle,
            ev
          )
        }
        if (tableHeader.headerGroup) {
          const groupHeight = ev.colNodes.filter(node => node.colIndex === -1)?.[0]?.height || 0
          ev.colsHierarchy.height =
            ev.colsHierarchy.height + ev.colsHierarchy.maxLevel * groupHeight
          ev.colLeafNodes.forEach(node => {
            if (node.level < ev.colsHierarchy.maxLevel) {
              const addHeight = ev.colsHierarchy.maxLevel - node.level
              node.height = node.height + addHeight * groupHeight
            }
          })
        }
      })
    }
    // 自适应铺满
    if (basicStyle.tableColumnMode === 'adapt') {
      newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, () => {
        newChart.store.set('lastLayoutResult', newChart.facet.layoutResult)
      })
      newChart.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev: LayoutResult) => {
        const lastLayoutResult = newChart.store.get('lastLayoutResult') as LayoutResult
        if (lastLayoutResult) {
          // 拖动表头 resize
          const widthByFieldValue = newChart.options.style?.colCfg?.widthByFieldValue
          const lastLayoutWidthMap: Record<string, number> =
            lastLayoutResult?.colLeafNodes.reduce((p, n) => {
              p[n.value] = widthByFieldValue?.[n.value] ?? n.width
              return p
            }, {}) || {}
          const totalWidth = ev.colLeafNodes.reduce((p, n) => {
            n.width = lastLayoutWidthMap[n.value] || n.width
            n.x = p
            return p + n.width
          }, 0)
          // 处理分组的单元格，宽度为所有叶子节点之和
          ev.colNodes.forEach(n => {
            if (n.colIndex === -1) {
              n.width = calcTreeWidth(n)
              n.x = getStartPosition(n)
            }
          })
          ev.colsHierarchy.width = totalWidth
          newChart.store.set('lastLayoutResult', undefined)
          return
        }
        // 第一次渲染初始化，把图片字段固定为 120 进行计算
        const urlFields = fields
          .filter(field => field.deType === 7 && !axisMap[field.dataeaseName]?.hide)
          .map(f => f.dataeaseName)
        const totalWidthWithImg = ev.colLeafNodes.reduce((p, n) => {
          return p + (urlFields.includes(n.field) ? 120 : n.width)
        }, 0)
        const containerWidth = containerDom.getBoundingClientRect().width
        if (containerWidth <= totalWidthWithImg) {
          // 图库计算的布局宽度已经大于等于容器宽度，不需要再扩大，但是需要处理非整数宽度值，不然会出现透明细线
          ev.colLeafNodes.reduce((p, n) => {
            n.width = Math.round(n.width)
            n.x = p
            return p + n.width
          }, 0)
          return
        }
        // 图片字段固定 120, 剩余宽度按比例均摊到其他字段进行扩大
        const totalWidthWithoutImg = ev.colLeafNodes.reduce((p, n) => {
          return p + (urlFields.includes(n.field) ? 0 : n.width)
        }, 0)
        const restWidth = containerWidth - urlFields.length * 120
        const scale = restWidth / totalWidthWithoutImg
        const totalWidth = ev.colLeafNodes.reduce((p, n) => {
          n.width = urlFields.includes(n.field) ? 120 : Math.round(n.width * scale)
          n.x = p
          return p + n.width
        }, 0)
        // 处理分组的单元格，宽度为所有叶子节点之和
        ev.colNodes.forEach(n => {
          if (n.colIndex === -1) {
            n.width = calcTreeWidth(n)
            n.x = getStartPosition(n)
          }
        })
        if (totalWidth > containerWidth) {
          ev.colLeafNodes[ev.colLeafNodes.length - 1].width -= totalWidth - containerWidth
        }
        ev.colsHierarchy.width = containerWidth
      })
    }
    // 空数据时表格样式
    configEmptyDataStyle(newChart, basicStyle, newData, container)
    // click
    newChart.on(S2Event.DATA_CELL_CLICK, ev => {
      const cell = newChart.getCell(ev.target)
      const meta = cell.getMeta() as ViewMeta
      const nameIdMap = fields.reduce((pre, next) => {
        pre[next['dataeaseName']] = next['id']
        return pre
      }, {})

      const rowData = newChart.dataSet.getRowData(meta)
      const dimensionList = []
      for (const key in rowData) {
        if (nameIdMap[key]) {
          dimensionList.push({ id: nameIdMap[key], value: rowData[key] })
        }
      }
      const param = {
        x: ev.x,
        y: ev.y,
        data: {
          dimensionList,
          name: nameIdMap[meta.valueField],
          sourceType: 'table-info',
          quotaList: []
        }
      }
      action(param)
    })
    // 合并的单元格直接复用数据单元格的事件
    newChart.on(S2Event.MERGED_CELLS_CLICK, e => newChart.emit(S2Event.DATA_CELL_CLICK, e))
    // tooltip
    const { show } = tooltip
    if (show) {
      newChart.on(S2Event.COL_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
      newChart.on(S2Event.DATA_CELL_HOVER, event => this.showTooltip(newChart, event, meta))
      newChart.on(S2Event.MERGED_CELLS_HOVER, event => this.showTooltip(newChart, event, meta))
      // touch
      this.configTouchEvent(newChart, drawOption, meta)
    }
    // header resize
    newChart.on(S2Event.LAYOUT_RESIZE_COL_WIDTH, ev => resizeAction(ev))
    // right click
    newChart.on(S2Event.GLOBAL_CONTEXT_MENU, event => copyContent(newChart, event, meta))
    // theme
    const customTheme = this.configTheme(chart)
    newChart.setThemeCfg({ theme: customTheme })
    return newChart
  }

  protected configTheme(chart: Chart): S2Theme {
    const theme = super.configTheme(chart)
    const { basicStyle, tableCell, tableHeader } = parseJson(chart.customAttr)
    if (tableCell.mergeCells) {
      const tableFontColor = hexColorToRGBA(tableCell.tableFontColor, basicStyle.alpha)
      let tableItemBgColor = tableCell.tableItemBgColor
      if (!isAlphaColor(tableItemBgColor)) {
        tableItemBgColor = hexColorToRGBA(tableItemBgColor, basicStyle.alpha)
      }
      const { tableBorderColor } = basicStyle
      const { tableItemAlign, tableItemFontSize } = tableCell
      const fontStyle = tableCell.isItalic ? 'italic' : 'normal'
      const fontWeight = tableCell.isBolder === false ? 'normal' : 'bold'
      const mergeCellTheme: S2Theme = {
        dataCell: {
          cell: {
            crossBackgroundColor: tableItemBgColor
          }
        },
        mergedCell: {
          cell: {
            backgroundColor: tableItemBgColor,
            crossBackgroundColor: tableItemBgColor,
            horizontalBorderColor: tableBorderColor,
            verticalBorderColor: tableBorderColor,
            horizontalBorderWidth: tableCell.showHorizonBorder ? 1 : 0,
            verticalBorderWidth: tableCell.showVerticalBorder ? 1 : 0
          },
          bolderText: {
            fill: tableFontColor,
            textAlign: tableItemAlign,
            fontSize: tableItemFontSize,
            fontStyle,
            fontWeight
          },
          text: {
            fill: tableFontColor,
            textAlign: tableItemAlign,
            fontSize: tableItemFontSize,
            fontStyle,
            fontWeight
          },
          measureText: {
            fill: tableFontColor,
            textAlign: tableItemAlign,
            fontSize: tableItemFontSize,
            fontStyle,
            fontWeight
          },
          seriesText: {
            fill: tableFontColor,
            textAlign: tableItemAlign,
            fontSize: tableItemFontSize,
            fontStyle,
            fontWeight
          }
        }
      }
      merge(theme, mergeCellTheme)
    }
    if (tableCell.tableItemAlign === 'custom') {
      const { alignConfig } = tableCell
      const alignMap = alignConfig.reduce((p, n) => {
        p[n.id] = n.align
        return p
      }, {})
      merge(theme, {
        dataCellAlignConfig: alignMap
      })
    }
    if (tableHeader.tableHeaderAlign === 'custom') {
      const { alignConfig } = tableHeader
      const alignMap = alignConfig.reduce((p, n) => {
        p[n.id] = n.align
        return p
      }, {})
      merge(theme, {
        colCellAlignConfig: alignMap
      })
    }
    return theme
  }

  protected configSummaryRowAndIndex(
    chart: Chart,
    pageInfo: PageInfo,
    s2Options: S2Options,
    s2DataConfig: S2DataConfig
  ) {
    const { tableHeader, basicStyle, tableCell } = parseJson(chart.customAttr)
    const fields = chart.data?.fields ?? []
    // 开启序号之后，第一列就是序号列，修改 label 即可
    if (s2Options.showSeriesNumber) {
      let indexLabel = tableHeader.indexLabel
      if (!indexLabel) {
        indexLabel = ''
      }
      s2Options.layoutCoordinate = (_, __, col) => {
        if (col.colIndex === 0 && col.rowIndex === 0) {
          col.label = indexLabel
          col.value = indexLabel
        }
      }
    }
    const { showSummary, summaryLabel } = basicStyle
    const data = s2DataConfig.data
    const xAxis = chart.xAxis
    if (showSummary && data?.length) {
      // 设置汇总行高度和表头一致
      const heightByField = {}
      heightByField[data.length] = tableHeader.tableTitleHeight
      s2Options.style.rowCfg = { heightByField }
      // 计算汇总加入到数据里，冻结最后一行
      s2Options.frozenTrailingRowCount = 1
      const axis = filter(xAxis, axis => [2, 3, 4].includes(axis.deType))
      const summaryObj = getSummaryRow(
        data,
        axis,
        basicStyle.seriesSummary,
        chart.data.customSumResult
      ) as any
      data.push(summaryObj)
    }
    const { mergeCells } = tableCell
    const mergedCellsInfoMap: Record<string, boolean> = {}
    if (mergeCells) {
      s2Options.mergedCellsInfo?.reduce((p, n) => {
        n.forEach(cell => {
          p[`${cell.rowIndex}-${cell.colIndex}`] = true
        })
        return p
      }, mergedCellsInfoMap)
    }
    s2Options.dataCell = viewMeta => {
      // 总计行处理
      if (showSummary && viewMeta.rowIndex === data.length - 1) {
        if (viewMeta.colIndex === 0) {
          if (tableHeader.showIndex) {
            viewMeta.fieldValue = summaryLabel ?? t('chart.total_show')
          } else {
            // 第一列不是数值类型的，显示总计
            if (![2, 3, 4].includes(xAxis?.[0]?.deType)) {
              viewMeta.fieldValue = summaryLabel ?? t('chart.total_show')
            }
          }
        }
        return new SummaryCell(viewMeta, viewMeta?.spreadsheet)
      }
      const field = fields.find(f => f.dataeaseName === viewMeta.valueField)
      if (field?.deType === 7 && chart.showPosition !== 'dialog') {
        return new ImageCell(viewMeta, viewMeta?.spreadsheet)
      }
      if (viewMeta.colIndex === 0 && s2Options.showSeriesNumber) {
        if (tableCell.mergeCells) {
          viewMeta.fieldValue = getRowIndex(s2Options.mergedCellsInfo, viewMeta)
        } else {
          viewMeta.fieldValue =
            pageInfo.pageSize * (pageInfo.currentPage - 1) + viewMeta.rowIndex + 1
        }
      }
      // 配置文本自动换行参数
      viewMeta.autoWrap = tableCell.mergeCells ? false : basicStyle.autoWrap
      viewMeta.maxLines = basicStyle.maxLines
      // 合并单元格标记
      if (mergeCells && mergedCellsInfoMap[`${viewMeta.rowIndex}-${viewMeta.colIndex}`]) {
        viewMeta.isMergedCell = true
      }
      return new CustomDataCell(viewMeta, viewMeta?.spreadsheet)
    }
  }

  setupDefaultOptions(chart: ChartObj): ChartObj {
    const customAttr = parseJson(chart.customAttr)
    if (customAttr.basicStyle.tableColumnMode === 'colAdapt') {
      customAttr.basicStyle.tableColumnMode = 'adapt'
    }
    return chart
  }

  constructor() {
    super('table-info', [])
  }
}
