import { nextTick } from 'vue'
import XEUtils from 'xe-utils'
import { VxeUI } from '../../../ui'
import { toFilters, handleFieldOrColumn, getRefElem } from '../../src/util'
import { toCssUnit, triggerEvent, updatePanelPlacement } from '../../../ui/src/dom'
import { isEnableConf } from '../../../ui/src/utils'

import type { TableFilterMethods, TableFilterPrivateMethods, VxeTableDefines } from '../../../../types'

const { renderer, hooks } = VxeUI

const tableFilterMethodKeys: (keyof TableFilterMethods)[] = ['openFilter', 'setFilter', 'clearFilter', 'saveFilter', 'saveFilterByEvent', 'saveFilterPanel', 'saveFilterPanelByEvent', 'resetFilter', 'resetFilterByEvent', 'resetFilterPanel', 'resetFilterPanelByEvent', 'getCheckedFilters', 'updateFilterOptionStatus']

const sourceType = 'table'

hooks.add('tableFilterModule', {
  setupTable ($xeTable) {
    const $xeGrid = $xeTable.xeGrid
    const $xeGantt = $xeTable.xeGantt
    const $xeGGWrapper = $xeGrid || $xeGantt

    const { props, reactData, internalData } = $xeTable
    const { refElem, refTableFilter } = $xeTable.getRefMaps()
    const { computeFilterOpts, computeMouseOpts } = $xeTable.getComputeMaps()

    const updatePopupStyle = () => {
      const { filterStore } = reactData
      const { elemStore } = internalData
      const { column, targetEl } = filterStore
      if (!column) {
        return nextTick()
      }
      const tableEl = refElem.value
      const filterOpts = computeFilterOpts.value
      const { maxHeight: customMaxHeight, transfer, zIndex } = filterOpts
      const filterRender = column ? column.filterRender : null
      const compConf = filterRender && isEnableConf(filterRender) ? renderer.get(filterRender.name) : null

      const headerScrollElem = getRefElem(elemStore['main-header-scroll'])
      if (!headerScrollElem) {
        return nextTick()
      }
      const tableFilter = refTableFilter.value
      const filterWrapperElem = tableFilter ? tableFilter.getRefMaps().refElem.value as HTMLDivElement : null
      if (!filterWrapperElem) {
        return nextTick()
      }
      const filterHeadElem = filterWrapperElem.querySelector<HTMLDivElement>('.vxe-table--filter-header')
      const filterFootElem = filterWrapperElem.querySelector<HTMLDivElement>('.vxe-table--filter-footer')

      /**
       * 是否显示筛选按钮图标
       * 如果不存在图标，则相对单元格居中显示
       */
      const handleStyle = () => {
        let currEl = targetEl
        if (!targetEl) {
          const headerWrapperElem = getRefElem(elemStore[`${column.fixed || 'main'}-header-wrapper`] || elemStore['main-header-wrapper'])
          const colEl = headerWrapperElem ? headerWrapperElem.querySelector<HTMLDivElement>(`.vxe-header--column.${column.id}`) : null
          if (colEl) {
            currEl = colEl
          }
        }
        if (!currEl) {
          return nextTick()
        }

        const btnRect = currEl.getBoundingClientRect()
        const tableRect = tableEl.getBoundingClientRect()
        let left = 0
        let top = 0
        let maxHeight = 0
        let panelStyle: Record<string, string | number> = {}
        if (transfer) {
          const ppObj = updatePanelPlacement(currEl, filterWrapperElem, {
            defaultPlacement: 'bottom',
            isMinWidth: false,
            teleportTo: transfer
          })
          panelStyle = ppObj.style
        } else {
          const filterWidth = filterWrapperElem.offsetWidth
          const centerWidth = targetEl ? filterWidth / 2 : ((filterWidth - column.renderWidth) / 2)
          left = btnRect.left - tableRect.left - centerWidth
          top = btnRect.top - tableRect.top + currEl.clientHeight
          maxHeight = Math.max(40, tableEl.clientHeight - top - (filterHeadElem ? filterHeadElem.clientHeight : 0) - (filterFootElem ? filterFootElem.clientHeight : 0) - 14)
          if (left < 1) {
            left = 1
          } else if (left > (tableEl.clientWidth - filterWidth - 1)) {
            left = tableEl.clientWidth - filterWidth - 1
          }
          if ($xeGGWrapper) {
            const wrapperEl = $xeGGWrapper.getRefMaps().refElem.value
            if (wrapperEl) {
              const wrapperRect = wrapperEl.getBoundingClientRect()
              top += tableRect.top - wrapperRect.top
            }
          }
          panelStyle = {
            top: toCssUnit(top),
            left: toCssUnit(left)
          }
        }
        if (zIndex) {
          panelStyle.zIndex = zIndex
        }
        filterStore.style = panelStyle
        // 筛选面板是自适应表格高度
        if (compConf ? !compConf.tableFilterAutoHeight : false) {
          maxHeight = 0
        } else {
          if (customMaxHeight) {
            maxHeight = customMaxHeight > maxHeight ? maxHeight : customMaxHeight
          }
        }
        // 判断面板不能大于表格高度
        filterStore.maxHeight = maxHeight
      }
      handleStyle()
      return nextTick().then(() => {
        handleStyle()
      })
    }
    const filterPrivateMethods: TableFilterPrivateMethods = {
      checkFilterOptions () {
        const { filterStore } = reactData
        const { column } = filterStore
        if (column) {
          const filterOptions = (column.filters || []) as VxeTableDefines.FilterOption[]
          filterStore.isAllSelected = filterOptions.every((item) => item._checked)
          filterStore.isIndeterminate = !filterStore.isAllSelected && filterOptions.some((item) => item._checked)
        }
      },
      handleOpenFilterColumn (evnt: MouseEvent, btnEl: HTMLDivElement | null, colEl: HTMLDivElement, column: VxeTableDefines.ColumnInfo, params: any) {
        const { initStore, filterStore } = reactData
        if (filterStore.column === column && filterStore.visible) {
          filterStore.visible = false
        } else {
          $xeTable.handleFilterOptions(column)
          internalData._currFilterParams = params
          filterStore.targetEl = btnEl
          filterStore.style = null
          filterStore.visible = true
          initStore.filter = true
        }
        nextTick().then(() => {
          updatePopupStyle()
        })
        $xeTable.dispatchEvent('filter-visible', { column, field: column.field, property: column.field, filterList: $xeTable.getCheckedFilters(), visible: filterStore.visible }, evnt)
      },
      /**
       * 点击筛选事件
       * 当筛选图标被点击时触发
       * 更新选项是否全部状态
       * 打开筛选面板
       * @param {Event} evnt 事件
       * @param {ColumnInfo} column 列配置
       * @param {Object} params 参数
       */
      triggerFilterEvent (evnt: MouseEvent, column: VxeTableDefines.ColumnInfo, params) {
        const { elemStore } = internalData
        const { fixed } = column
        const headerWrapperElem = getRefElem(elemStore[`${fixed || 'main'}-header-wrapper`] || elemStore['main-header-wrapper'])
        const btnElem = evnt.currentTarget as HTMLDivElement
        const colEl = headerWrapperElem ? headerWrapperElem.querySelector<HTMLElement>(`.vxe-header--column.${column.id}`) : null
        $xeTable.handleOpenFilterColumn(evnt, btnElem, colEl || btnElem, column, params)
      },
      handleClearFilter (column) {
        if (column) {
          const { filters, filterRender } = column
          if (filters) {
            const compConf = isEnableConf(filterRender) ? renderer.get(filterRender.name) : null
            const frMethod = column.filterResetMethod || (compConf ? (compConf.tableFilterResetMethod || compConf.filterResetMethod) : null)
            filters.forEach((item: any) => {
              item._checked = false
              item.checked = false
              if (!frMethod) {
                item.data = XEUtils.clone(item.resetValue, true)
              }
            })
            if (frMethod) {
              frMethod({ options: filters, column, $table: $xeTable })
            }
          }
        }
      },
      handleColumnConfirmFilter (column, evnt) {
        const { mouseConfig } = props
        const { scrollXLoad: oldScrollXLoad, scrollYLoad: oldScrollYLoad } = reactData
        const filterOpts = computeFilterOpts.value
        const mouseOpts = computeMouseOpts.value
        const { field, filters } = column
        const filterOptions = filters || []
        const values: any[] = []
        const datas: any[] = []
        filterOptions.forEach((item) => {
          if (item.checked) {
            values.push(item.value)
            datas.push(item.data)
          }
        })
        const filterList = $xeTable.getCheckedFilters()
        const params = { $table: $xeTable, $event: evnt as Event, column, field, property: field, values, datas, filters: filterList, filterList }
        // 如果是服务端筛选，则跳过本地筛选处理
        if (!filterOpts.remote) {
          $xeTable.handleTableData(true)
          $xeTable.checkSelectionStatus()
        }
        if (mouseConfig && mouseOpts.area && $xeTable.handleFilterEvent) {
          $xeTable.handleFilterEvent(evnt as Event, params)
        }
        if (evnt) {
          $xeTable.dispatchEvent('filter-change', params, evnt)
        }
        $xeTable.closeFilter()
        return $xeTable.updateFooter().then(() => {
          const { scrollXLoad, scrollYLoad } = reactData
          if ((oldScrollXLoad || scrollXLoad) || (oldScrollYLoad || scrollYLoad)) {
            if (oldScrollXLoad || scrollXLoad) {
              $xeTable.updateScrollXSpace()
            }
            if (oldScrollYLoad || scrollYLoad) {
              $xeTable.updateScrollYSpace()
            }
            return $xeTable.refreshScroll()
          }
        }).then(() => {
          $xeTable.updateCellAreas()
          return $xeTable.recalculate(true)
        }).then(() => {
          // 存在滚动行为未结束情况
          setTimeout(() => $xeTable.recalculate(), 50)
        })
      },
      /**
       * 确认筛选
       * 当筛选面板中的确定按钮被按下时触发
       */
      confirmFilterEvent (evnt, column) {
        if (column) {
          $xeTable.handleColumnConfirmFilter(column, evnt)
        }
      },
      // （单选）筛选发生改变
      handleFilterChangeRadioOption (evnt, checked, item) {
        const { filterStore } = reactData
        const { column } = filterStore
        if (column) {
          const filterOptions = (column.filters || []) as VxeTableDefines.FilterOption[]
          filterOptions.forEach((option) => {
            option._checked = false
          })
          item._checked = checked
          $xeTable.checkFilterOptions()
          $xeTable.handleFilterConfirmFilter(evnt, column)
        }
      },
      // （多选）筛选发生改变
      handleFilterChangeMultipleOption (evnt, checked, item) {
        item._checked = checked
        $xeTable.checkFilterOptions()
      },
      // 筛选发生改变
      handleFilterChangeOption (evnt, checked, item) {
        const { filterStore } = reactData
        const { fullColumnIdData } = internalData
        let column = filterStore.column
        if (!column) {
          const colRest = fullColumnIdData[item._colId]
          if (colRest) {
            column = colRest.column
            filterStore.column = column
          }
        }
        if (column) {
          if (column.filterMultiple) {
            $xeTable.handleFilterChangeMultipleOption(evnt, checked, item)
          } else {
            $xeTable.handleFilterChangeRadioOption(evnt, checked, item)
          }
        }
      },
      // 确认筛选
      handleFilterConfirmFilter (evnt, column) {
        if (column) {
          const filterOptions = (column.filters || []) as VxeTableDefines.FilterOption[]
          filterOptions.forEach((option) => {
            option.checked = option._checked
          })
          $xeTable.confirmFilterEvent(evnt, column)
        }
      },
      /**
       * 重置筛选
       * 当筛选面板中的重置按钮被按下时触发
       */
      handleFilterResetFilter (evnt: Event, column) {
        if (column) {
          $xeTable.handleClearFilter(column)
          $xeTable.confirmFilterEvent(evnt, column)
          if (evnt) {
            $xeTable.dispatchEvent('clear-filter', { filterList: [] }, evnt)
          }
        }
      },
      handleFilterStyle () {
        const { filterStore } = reactData
        if (!filterStore.visible) {
          return nextTick()
        }
        return updatePopupStyle()
      }
    }

    const filterMethods: TableFilterMethods = {
      /**
       * 手动弹出筛选面板
       * @param column
       */
      openFilter (fieldOrColumn) {
        const column = handleFieldOrColumn($xeTable, fieldOrColumn)
        if (column && column.filters) {
          const { elemStore } = internalData
          const { fixed, filters } = column
          return $xeTable.scrollToColumn(column).then(() => {
            const headerWrapperElem = getRefElem(elemStore[`${fixed || 'main'}-header-wrapper`] || elemStore['main-header-wrapper'])
            if (headerWrapperElem) {
              const filterBtnElem = headerWrapperElem.querySelector<HTMLElement>(`.vxe-header--column.${column.id} .vxe-cell--filter`)
              if (filterBtnElem) {
                triggerEvent(filterBtnElem, 'click')
              } else {
                const colEl = headerWrapperElem.querySelector<HTMLElement>(`.vxe-header--column.${column.id}`)
                if (colEl) {
                  let firstFilterOption: VxeTableDefines.FilterOption | null = null
                  if (filters) {
                    firstFilterOption = filters[0] as VxeTableDefines.FilterOption
                  }
                  const params = {
                    $table: $xeTable,
                    $grid: $xeGrid,
                    $gantt: $xeGantt,
                    $rowIndex: -1,
                    column,
                    columnIndex: $xeTable.getColumnIndex(column),
                    $columnIndex: $xeTable.getVMColumnIndex(column),
                    _columnIndex: $xeTable.getVTColumnIndex(column),
                    firstFilterOption: firstFilterOption as VxeTableDefines.FilterOption,
                    fixed,
                    source: sourceType,
                    type: 'header'
                    // isHidden
                    // hasFilter
                  }
                  $xeTable.handleOpenFilterColumn(new Event('click'), null, colEl, column, params)
                }
              }
            }
          })
        }
        return nextTick()
      },
      /**
       * 修改筛选条件列表
       * @param {ColumnInfo} fieldOrColumn 列或字段名
       * @param {Array} options 选项
       */
      setFilter (fieldOrColumn, options, isUpdate) {
        const { filterStore } = reactData
        const column = handleFieldOrColumn($xeTable, fieldOrColumn)
        if (column && column.filters) {
          column.filters = toFilters(options || [], column.id)
          if (isUpdate) {
            return $xeTable.handleColumnConfirmFilter(column, null)
          } else {
            if (filterStore.visible) {
              $xeTable.handleFilterOptions(column)
            }
          }
        }
        return nextTick()
      },
      /**
       * 清空指定列的筛选条件
       * 如果为空则清空所有列的筛选条件
       * @param {String} fieldOrColumn 列或字段名
       */
      clearFilter (fieldOrColumn) {
        const { filterStore } = reactData
        const { tableFullColumn } = internalData
        const filterOpts = computeFilterOpts.value
        let column
        if (fieldOrColumn) {
          column = handleFieldOrColumn($xeTable, fieldOrColumn)
          if (column) {
            $xeTable.handleClearFilter(column)
          }
        } else {
          tableFullColumn.forEach($xeTable.handleClearFilter)
        }
        if (!fieldOrColumn || column !== filterStore.column) {
          Object.assign(filterStore, {
            isAllSelected: false,
            isIndeterminate: false,
            style: null,
            options: [],
            column: null,
            multiple: false,
            visible: false
          })
        }
        if (!filterOpts.remote) {
          return $xeTable.updateData()
        }
        return nextTick()
      },
      saveFilter (fieldOrColumn) {
        if (fieldOrColumn) {
          const column = handleFieldOrColumn($xeTable, fieldOrColumn)
          $xeTable.handleFilterConfirmFilter(null, column)
        }
        return nextTick()
      },
      saveFilterByEvent (evnt, fieldOrColumn) {
        if (fieldOrColumn) {
          const column = handleFieldOrColumn($xeTable, fieldOrColumn)
          $xeTable.handleFilterConfirmFilter(evnt, column)
        }
        return nextTick()
      },
      resetFilter (fieldOrColumn) {
        if (fieldOrColumn) {
          const column = handleFieldOrColumn($xeTable, fieldOrColumn)
          $xeTable.handleFilterResetFilter(null, column)
        }
        return nextTick()
      },
      resetFilterByEvent (evnt, fieldOrColumn) {
        if (fieldOrColumn) {
          const column = handleFieldOrColumn($xeTable, fieldOrColumn)
          $xeTable.handleFilterResetFilter(evnt, column)
        }
        return nextTick()
      },
      saveFilterPanel () {
        const { filterStore } = reactData
        $xeTable.handleFilterConfirmFilter(null, filterStore.column || null)
        return nextTick()
      },
      saveFilterPanelByEvent (evnt) {
        const { filterStore } = reactData
        $xeTable.handleFilterConfirmFilter(evnt, filterStore.column || null)
        return nextTick()
      },
      resetFilterPanel () {
        const { filterStore } = reactData
        $xeTable.handleFilterResetFilter(null, filterStore.column || null)
        return nextTick()
      },
      resetFilterPanelByEvent (evnt) {
        const { filterStore } = reactData
        $xeTable.handleFilterResetFilter(evnt, filterStore.column || null)
        return nextTick()
      },
      getCheckedFilters () {
        const { tableFullColumn } = internalData
        const filterList: VxeTableDefines.FilterCheckedParams[] = []
        tableFullColumn.forEach((column) => {
          const { field, filters } = column
          const filterOptions = filters || []
          const valueList: any[] = []
          const dataList: any[] = []
          if (filterOptions) {
            filterOptions.forEach((item) => {
              if (item.checked) {
                valueList.push(item.value)
                dataList.push(item.data)
              }
            })
            if (valueList.length) {
              filterList.push({ column, field, property: field, values: valueList, datas: dataList })
            }
          }
        })
        return filterList
      },
      updateFilterOptionStatus (item: any, checked: boolean) {
        item._checked = checked
        item.checked = checked
        return nextTick()
      }
    }

    return { ...filterMethods, ...filterPrivateMethods }
  },
  setupGrid ($xeGrid) {
    return $xeGrid.extendTableMethods(tableFilterMethodKeys)
  },
  setupGantt ($xeGantt) {
    return $xeGantt.extendTableMethods(tableFilterMethodKeys)
  }
})
