import cx from 'classnames'
import React from 'react'
import { ExpansionCell, icons, InlineFlexCell } from '../../common-views'
import type { ArtColumn, PositionKeysMapType } from '../../interfaces'
import { internals } from '../../internals'
import { isLeafNode as standardIsLeafNode, mergeCellProps } from '../../utils'
import { TablePipeline } from '../pipeline'

export const treeMetaSymbol = Symbol('treeMetaSymbol')

export interface TreeModeFeatureOptions {
  /** 非受控用法：默认展开的 keys */
  defaultOpenKeys?: string[]

  /** 受控用法：当前展开的 keys */
  openKeys?: string[]

  /** 受控用法：展开 keys 改变的回调 */
  onChangeOpenKeys?(nextKeys: string[], key: string, action: 'expand' | 'collapse'): void

  /** 自定义叶子节点的判定逻辑 */
  isLeafNode?(node: any, nodeMeta: { depth: number; expanded: boolean; rowKey: string, childField?: string }): boolean

  /** icon 的缩进值。一般为负数，此时 icon 将向左偏移，默认从 pipeline.ctx.indents 中获取 */
  iconIndent?: number

  /** icon 与右侧文本的距离，默认从 pipeline.ctx.indents 中获取 */
  iconGap?: number

  /** 每一级缩进产生的距离，默认从 pipeline.ctx.indents 中获取 */
  indentSize?: number

  /** 点击事件的响应区域 */
  clickArea?: 'cell' | 'content' | 'icon'

  /** 是否对触发展开/收拢的 click 事件调用 event.stopPropagation() */
  stopClickEventPropagation?: boolean

  /** 指定表格每一行元信息的记录字段 */
  treeMetaKey?: string | symbol

  /**
   * 展开符号添加的位置
   * 默认第一项
  */
  positionKey?: string | number;
  /**根据层级进行处理展开节点*/
  positionKeysMap?: PositionKeysMapType
}

export function treeMode(opts: TreeModeFeatureOptions = {}) {

  return function treeModeStep(pipeline: TablePipeline) {
    const stateKey = 'treeMode'
    const ctx = pipeline.ctx
    const positionKeysMap = opts.positionKeysMap ?? {};
    // 对这个数据进行处理成需要的格式
    const fieldMaps = Array.from(new Set(Object.entries(positionKeysMap).map(([_, value]) => value.code)))

    const primaryKey = pipeline.ensurePrimaryKey('treeMode') as string
    if (typeof primaryKey !== 'string') {
      throw new Error('treeMode 仅支持字符串作为 primaryKey')
    }

    const openKeys: string[] = opts.openKeys ?? pipeline.getStateAtKey(stateKey) ?? opts.defaultOpenKeys ?? []
    const openKeySet = new Set(openKeys)
    const onChangeOpenKeys: TreeModeFeatureOptions['onChangeOpenKeys'] = (nextKeys: string[], key, action) => {
      opts.onChangeOpenKeys?.(nextKeys, key, action)
      pipeline.setStateAtKey(stateKey, nextKeys, { key, action })
    }

    const toggle = (rowKey: string) => {
      const expanded = openKeySet.has(rowKey)
      if (expanded) {
        onChangeOpenKeys(
          openKeys.filter((key) => key !== rowKey),
          rowKey,
          'collapse',
        )
      } else {
        onChangeOpenKeys([...openKeys, rowKey], rowKey, 'expand')
      }
    }
    const isLeafNode = opts.isLeafNode ?? standardIsLeafNode
    const clickArea = opts.clickArea ?? 'cell'
    const treeMetaKey = opts.treeMetaKey ?? treeMetaSymbol
    const stopClickEventPropagation = Boolean(opts.stopClickEventPropagation)

    // indents
    const iconWidth = ctx.indents.iconWidth
    const iconIndent = opts.iconIndent ?? ctx.indents.iconIndent
    const iconGap = opts.iconGap ?? ctx.indents.iconGap
    const indentSize = opts.indentSize ?? ctx.indents.indentSize
    const positionKey = opts.positionKey;

    return pipeline.mapDataSource(processDataSource).mapColumns(processColumns)

    function processDataSource(input: any[]) {
      const result: any[] = []
      dfs(input, 0)

      function dfs(nodes: any[], depth: number) {
        if (nodes == null) {
          return
        }
        for (const node of nodes) {
          const rowKey = node[primaryKey]
          const expanded = openKeySet.has(rowKey)
          const item = positionKeysMap?.[depth];

          const isLeaf = isLeafNode(node, { depth, expanded, rowKey, childField: item?.childField || "children" })
          const treeMeta = { depth, isLeaf, expanded, rowKey }
          result.push({ [treeMetaKey]: treeMeta, ...node })
          if (!isLeaf && expanded) {
            if (item && item?.childField) {
              dfs(node[item.childField], depth + 1)
            } else {
              dfs(node.children, depth + 1)
            }
          }
        }
      }
      return result
    }

    function processColumns(columns: ArtColumn[]) {
      if (columns.length === 0) {
        return columns
      }
      const newColumns = [...columns]
      let [firstCol] = newColumns
      let positionIndex = 0;

      if (typeof positionKey === "string") {
        const colItemIndex = newColumns.findIndex((ite) => ite.code === positionKey);
        if (colItemIndex >= 0) {
          positionIndex = colItemIndex
          firstCol = newColumns[colItemIndex]
        }
      } else if (typeof positionKey === 'number') {
        const colItem = newColumns[positionKey]
        if (colItem) {
          positionIndex = positionKey
          firstCol = colItem
        }
      }
      const render = (value: any, record: any, recordIndex: number, firstCol: ArtColumn) => {
        const content = internals.safeRender(firstCol, record, recordIndex)
        if (record[treeMetaKey] == null) {
          // 没有 treeMeta 信息的话，就返回原先的渲染结果
          return content
        }
        const { rowKey, depth, isLeaf, expanded } = record[treeMetaKey]
        const indent = iconIndent + depth * indentSize
        if (isLeaf) {
          return (
            <InlineFlexCell className="expansion-cell leaf">
              <span style={{ marginLeft: indent + iconWidth + iconGap }}>{content}</span>
            </InlineFlexCell>
          )
        }

        const item = positionKeysMap?.[depth]
        if (item) {
          // 判断当前是否是渲染字段，如果是则进行渲染图标，否则不进行渲染
          if (item.code !== firstCol.code) {
            return (
              <InlineFlexCell className="expansion-cell leaf">
                <span style={{ marginLeft: indent + iconWidth + iconGap }}>{content}</span>
              </InlineFlexCell>
            )
          }
        }

        const onClick = (e: React.MouseEvent) => {
          if (stopClickEventPropagation) {
            e.stopPropagation()
          }
          toggle(rowKey)
        }

        const expandCls = expanded ? 'expanded' : 'collapsed'
        return (
          <ExpansionCell
            className={cx('expansion-cell', expandCls)}
            style={{
              cursor: clickArea === 'content' ? 'pointer' : undefined,
            }}
            onClick={clickArea === 'content' ? onClick : undefined}
          >
            <icons.CaretRight
              className={cx('expansion-icon', expandCls)}
              style={{
                cursor: clickArea === 'icon' ? 'pointer' : undefined,
                marginLeft: indent,
                marginRight: iconGap,
              }}
              onClick={clickArea === 'icon' ? onClick : undefined}
            />
            {content}
          </ExpansionCell>
        )
      }
      const getCellProps = (value: any, record: any, rowIndex: number, firstCol: ArtColumn) => {
        const prevProps = internals.safeGetCellProps(firstCol, record, rowIndex)
        if (record[treeMetaKey] == null) {
          // 没有 treeMeta 信息的话，就返回原先的 cellProps
          return prevProps
        }

        const { isLeaf, rowKey, depth } = record[treeMetaKey]
        if (isLeaf) {
          return prevProps
        }
        const item = positionKeysMap?.[depth]
        if (item) {
          // 判断当前是否是渲染字段，如果是则进行渲染图标，否则不进行渲染
          if (item.code !== firstCol.code) {
            return prevProps
          }
        }

        return mergeCellProps(prevProps, {
          onClick(e) {
            if (stopClickEventPropagation) {
              e.stopPropagation()
            }
            toggle(rowKey)
          },
          style: { cursor: 'pointer' },
        })
      }
      /**多个字段映射时使用*/
      if (fieldMaps.length > 0) {
        for (const field of fieldMaps) {
          const colItemIndex = newColumns.findIndex((ite) => ite.code === field);
          if (colItemIndex >= 0) {
            const firstCol = newColumns[colItemIndex]
            const newItem = {
              ...firstCol,
              title: (
                <span style={{ marginLeft: iconIndent + iconWidth + iconGap, display: "inline-block" }}>{internals.safeRenderHeader(firstCol)}</span>
              ),
              render: (value: any, record: any, recordIndex: number) => render(value, record, recordIndex, firstCol),
              getCellProps: (value: any, record: any, rowIndex: number) => clickArea === 'cell' ? getCellProps(value, record, rowIndex, firstCol) : firstCol.getCellProps?.(value, record, rowIndex),
            }
            newColumns[colItemIndex] = { ...newItem }
          }
        }
      } else {
        const newItem = {
          ...firstCol,
          title: (
            <span style={{ marginLeft: iconIndent + iconWidth + iconGap }}>{internals.safeRenderHeader(firstCol)}</span>
          ),
          render: (value: any, record: any, recordIndex: number) => render(value, record, recordIndex, firstCol),
          getCellProps: (value: any, record: any, rowIndex: number) => clickArea === 'cell' ? getCellProps(value, record, rowIndex, firstCol) : firstCol.getCellProps?.(value, record, rowIndex),
        }
        newColumns[positionIndex] = { ...newItem }
      }
      return [...newColumns];
    }
  }
}
