import { nextTick } from 'vue'
import XEUtils from 'xe-utils'
import { VXETable } from '../../v-x-e-table'
import { toFilters, handleFieldOrColumn } from '../../table/src/util'
import { getDomNode, triggerEvent } from '../../tools/dom'

import { VxeGlobalHooksHandles, TableFilterMethods, TableFilterPrivateMethods } from '../../../types/all'

const tableFilterMethodKeys: (keyof TableFilterMethods)[] = ['setFilter', 'clearFilter', 'getCheckedFilters']

const tableFilterHook: VxeGlobalHooksHandles.HookOptions = {
  setupTable ($xetable) {
    const { props, reactData, internalData } = $xetable
    const { refTableBody, refTableFilter } = $xetable.getRefMaps()
    const { computeFilterOpts, computeMouseOpts } = $xetable.getComputeMaps()

    const filterPrivateMethods: TableFilterPrivateMethods = {
      checkFilterOptions () {
        const { filterStore } = reactData
        filterStore.isAllSelected = filterStore.options.every((item: any) => item._checked)
        filterStore.isIndeterminate = !filterStore.isAllSelected && filterStore.options.some((item: any) => item._checked)
      },
      /**
       * 点击筛选事件
       * 当筛选图标被点击时触发
       * 更新选项是否全部状态
       * 打开筛选面板
       * @param {Event} evnt 事件
       * @param {ColumnInfo} column 列配置
       * @param {Object} params 参数
       */
      triggerFilterEvent (evnt, column, params) {
        const { initStore, filterStore } = reactData
        if (filterStore.column === column && filterStore.visible) {
          filterStore.visible = false
        } else {
          const { target: targetElem, pageX } = evnt
          const { visibleWidth } = getDomNode()
          const { filters, filterMultiple, filterRender } = column
          const compConf = filterRender ? VXETable.renderer.get(filterRender.name) : null
          const filterRecoverMethod = column.filterRecoverMethod || (compConf ? compConf.filterRecoverMethod : null)
          internalData._currFilterParams = params
          Object.assign(filterStore, {
            multiple: filterMultiple,
            options: filters,
            column,
            style: null
          })
          // 复原状态
          filterStore.options.forEach((option: any) => {
            const { _checked, checked } = option
            option._checked = checked
            if (!checked && _checked !== checked) {
              if (filterRecoverMethod) {
                filterRecoverMethod({ option, column, $table: $xetable })
              }
            }
          })
          this.checkFilterOptions()
          filterStore.visible = true
          initStore.filter = true
          nextTick(() => {
            const tableBody = refTableBody.value
            const bodyElem = tableBody.$el as HTMLDivElement
            const tableFilter = refTableFilter.value
            const filterWrapperElem = tableFilter ? tableFilter.$el as HTMLDivElement : null
            let filterWidth = 0
            let filterHeight = 0
            let filterHeadElem: HTMLDivElement | null = null
            let filterFootElem: HTMLDivElement | null = null
            if (filterWrapperElem) {
              filterWidth = filterWrapperElem.offsetWidth
              filterHeight = filterWrapperElem.offsetHeight
              filterHeadElem = filterWrapperElem.querySelector('.vxe-table--filter-header')
              filterFootElem = filterWrapperElem.querySelector('.vxe-table--filter-footer')
            }
            const centerWidth = filterWidth / 2
            const minMargin = 10
            const maxLeft = bodyElem.clientWidth - filterWidth - minMargin
            let left, right
            const style: any = {
              top: `${targetElem.offsetTop + targetElem.offsetParent.offsetTop + targetElem.offsetHeight + 8}px`
            }
            // 判断面板不能大于表格高度
            let maxHeight = null
            if (filterHeight >= bodyElem.clientHeight) {
              maxHeight = Math.max(60, bodyElem.clientHeight - (filterFootElem ? filterFootElem.offsetHeight : 0) - (filterHeadElem ? filterHeadElem.offsetHeight : 0))
            }
            if (column.fixed === 'left') {
              left = targetElem.offsetLeft + targetElem.offsetParent.offsetLeft - centerWidth
            } else if (column.fixed === 'right') {
              right = (targetElem.offsetParent.offsetWidth - targetElem.offsetLeft) + (targetElem.offsetParent.offsetParent.offsetWidth - targetElem.offsetParent.offsetLeft) - column.renderWidth - centerWidth
            } else {
              left = targetElem.offsetLeft + targetElem.offsetParent.offsetLeft - centerWidth - bodyElem.scrollLeft
            }
            if (left) {
              const overflowWidth = (pageX + filterWidth - centerWidth + minMargin) - visibleWidth
              if (overflowWidth > 0) {
                left -= overflowWidth
              }
              style.left = `${Math.min(maxLeft, Math.max(minMargin, left))}px`
            } else if (right) {
              const overflowWidth = (pageX + filterWidth - centerWidth + minMargin) - visibleWidth
              if (overflowWidth > 0) {
                right += overflowWidth
              }
              style.right = `${Math.max(minMargin, right)}px`
            }
            filterStore.style = style
            filterStore.maxHeight = maxHeight
          })
        }
        $xetable.dispatchEvent('filter-visible', { column, field: column.field, property: column.field, filterList: $xetable.getCheckedFilters(), visible: filterStore.visible }, evnt)
      },
      handleClearFilter (column) {
        if (column) {
          const { filters, filterRender } = column
          if (filters) {
            const compConf = filterRender ? VXETable.renderer.get(filterRender.name) : null
            const filterResetMethod = column.filterResetMethod || (compConf ? compConf.filterResetMethod : null)
            filters.forEach((item: any) => {
              item._checked = false
              item.checked = false
              if (!filterResetMethod) {
                item.data = XEUtils.clone(item.resetValue, true)
              }
            })
            if (filterResetMethod) {
              filterResetMethod({ options: filters, column, $table: $xetable })
            }
          }
        }
      },
      /**
       * 确认筛选
       * 当筛选面板中的确定按钮被按下时触发
       * @param {Event} evnt 事件
       */
      confirmFilterEvent (evnt: Event) {
        const { mouseConfig } = props
        const { filterStore, scrollXLoad: oldScrollXLoad, scrollYLoad: oldScrollYLoad } = reactData
        const filterOpts = computeFilterOpts.value
        const mouseOpts = computeMouseOpts.value
        const { column } = filterStore
        const { field } = column
        const values: any[] = []
        const datas: any[] = []
        column.filters.forEach((item: any) => {
          if (item.checked) {
            values.push(item.value)
            datas.push(item.data)
          }
        })
        const filterList = $xetable.getCheckedFilters()
        const params = { $table: $xetable, $event: evnt, 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, params)
        }
        $xetable.dispatchEvent('filter-change', params, evnt)
        $xetable.closeFilter()
        $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)
        })
      }
    }

    const filterMethods: TableFilterMethods = {
      /**
       * 手动弹出筛选面板
       * @param column
       */
      openFilter (fieldOrColumn) {
        const column = handleFieldOrColumn($xetable, fieldOrColumn)
        if (column && column.filters) {
          const { elemStore } = internalData
          const { fixed } = column
          return $xetable.scrollToColumn(column).then(() => {
            const headerWrapperRef = elemStore[`${fixed || 'main'}-header-wrapper`] || elemStore['main-header-wrapper']
            const headerWrapperElem = headerWrapperRef ? headerWrapperRef.value : null
            if (headerWrapperElem) {
              const filterBtnElem = headerWrapperElem.querySelector(`.vxe-header--column.${column.id} .vxe-filter--btn`) as HTMLElement
              triggerEvent(filterBtnElem, 'click')
            }
          })
        }
        return nextTick()
      },
      /**
       * 修改筛选条件列表
       * @param {ColumnInfo} fieldOrColumn 列或字段名
       * @param {Array} options 选项
       */
      setFilter (fieldOrColumn, options) {
        const column = handleFieldOrColumn($xetable, fieldOrColumn)
        if (column && column.filters) {
          column.filters = toFilters(options || [])
        }
        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) {
            filterPrivateMethods.handleClearFilter(column)
          }
        } else {
          tableFullColumn.forEach(filterPrivateMethods.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()
      },
      getCheckedFilters () {
        const { tableFullColumn } = internalData
        const filterList: any[] = []
        tableFullColumn.forEach((column) => {
          const { field, filters } = column
          const valueList: any[] = []
          const dataList: any[] = []
          if (filters && filters.length) {
            filters.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
      }
    }

    return { ...filterMethods, ...filterPrivateMethods }
  },
  setupGrid ($xegrid) {
    return $xegrid.extendTableMethods(tableFilterMethodKeys)
  }
}

export default tableFilterHook
