import { nextTick } from 'vue'
import XEUtils from 'xe-utils'
import { VxeUI } from '../../../ui'
import { getDomNode, getAbsolutePos, getEventTargetNode } from '../../../ui/src/dom'
import { isEnableConf, hasChildrenList } from '../../../ui/src/utils'

import type { TableMenuMethods, TableMenuPrivateMethods } from '../../../../types'

const { menus, hooks, globalEvents, GLOBAL_EVENT_KEYS } = VxeUI

const tableMenuMethodKeys: (keyof TableMenuMethods)[] = ['closeMenu']

hooks.add('tableMenuModule', {
  setupTable ($xeTable) {
    const { xID, props, reactData, internalData } = $xeTable
    const { refElem, refTableFilter, refTableMenu } = $xeTable.getRefMaps()
    const { computeMouseOpts, computeIsContentMenu, computeMenuOpts } = $xeTable.getComputeMaps()

    const $xeGrid = $xeTable.xeGrid
    const $xeGantt = $xeTable.xeGantt
    const $xeGGWrapper = $xeGrid || $xeGantt

    let menuMethods = {} as TableMenuMethods
    let menuPrivateMethods = {} as TableMenuPrivateMethods

    /**
     * 显示快捷菜单
     */
    const handleOpenMenuEvent = (evnt: any, type: 'header' | 'body' | 'footer', params: any) => {
      const { ctxMenuStore } = reactData
      const isContentMenu = computeIsContentMenu.value
      const menuOpts = computeMenuOpts.value
      const config = menuOpts[type]
      const { zIndex, transfer, visibleMethod } = menuOpts
      if (config) {
        const { options, disabled } = config
        if (disabled) {
          evnt.preventDefault()
        } else if (isContentMenu && options && options.length) {
          params.options = options
          $xeTable.preventEvent(evnt, 'event.showMenu', params, () => {
            if (!visibleMethod || visibleMethod(params)) {
              evnt.preventDefault()
              $xeTable.updateZindex()
              const el = $xeGGWrapper ? $xeGGWrapper.getRefMaps().refElem.value : refElem.value
              if (!el) {
                return
              }
              const tableRect = el.getBoundingClientRect()
              const { scrollTop, scrollLeft, visibleHeight, visibleWidth } = getDomNode()

              let top = evnt.clientY - tableRect.y
              let left = evnt.clientX - tableRect.x
              if (transfer) {
                top = evnt.clientY + scrollTop
                left = evnt.clientX + scrollLeft
              }

              const handleVisible = () => {
                internalData._currMenuParams = params
                Object.assign(ctxMenuStore, {
                  visible: true,
                  list: options,
                  selected: null,
                  selectChild: null,
                  showChild: false,
                  style: {
                    zIndex: zIndex || internalData.tZindex,
                    top: `${top}px`,
                    left: `${left}px`
                  }
                })
                nextTick(() => {
                  const tableMenu = refTableMenu.value
                  const ctxElem = tableMenu.getRefMaps().refElem.value
                  const clientHeight = ctxElem.clientHeight
                  const clientWidth = ctxElem.clientWidth
                  const { boundingTop, boundingLeft } = getAbsolutePos(ctxElem)
                  const offsetTop = boundingTop + clientHeight - visibleHeight
                  const offsetLeft = boundingLeft + clientWidth - visibleWidth
                  if (offsetTop > -10) {
                    ctxMenuStore.style.top = `${Math.max(scrollTop + 2, top - clientHeight - 2)}px`
                  }
                  if (offsetLeft > -10) {
                    ctxMenuStore.style.left = `${Math.max(scrollLeft + 2, left - clientWidth - 2)}px`
                  }
                })
              }
              const { keyboard, row, column } = params
              if (keyboard && row && column) {
                $xeTable.scrollToRow(row, column).then(() => {
                  const cell = $xeTable.getCellElement(row, column)
                  if (cell) {
                    const { boundingTop, boundingLeft } = getAbsolutePos(cell)
                    top = boundingTop + scrollTop + Math.floor(cell.offsetHeight / 2)
                    left = boundingLeft + scrollLeft + Math.floor(cell.offsetWidth / 2)
                  }
                  handleVisible()
                })
              } else {
                handleVisible()
              }
            } else {
              menuMethods.closeMenu()
            }
          })
        }
      }
      $xeTable.closeFilter()
    }

    menuMethods = {
      /**
       * 关闭快捷菜单
       */
      closeMenu () {
        Object.assign(reactData.ctxMenuStore, {
          visible: false,
          selected: null,
          selectChild: null,
          showChild: false
        })
        return nextTick()
      }
    }

    menuPrivateMethods = {
      /**
       * 处理菜单的移动
       */
      moveCtxMenu (evnt, ctxMenuStore, property, hasOper, operRest, menuList) {
        let selectItem
        const selectIndex = XEUtils.findIndexOf(menuList, item => ctxMenuStore[property] === item)
        if (hasOper) {
          if (operRest && hasChildrenList(ctxMenuStore.selected)) {
            ctxMenuStore.showChild = true
          } else {
            ctxMenuStore.showChild = false
            ctxMenuStore.selectChild = null
          }
        } else if (globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_UP)) {
          for (let len = selectIndex - 1; len >= 0; len--) {
            if (menuList[len].visible !== false) {
              selectItem = menuList[len]
              break
            }
          }
          ctxMenuStore[property] = selectItem || menuList[menuList.length - 1]
        } else if (globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ARROW_DOWN)) {
          for (let index = selectIndex + 1; index < menuList.length; index++) {
            if (menuList[index].visible !== false) {
              selectItem = menuList[index]
              break
            }
          }
          ctxMenuStore[property] = selectItem || menuList[0]
        } else if (ctxMenuStore[property] && (globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.ENTER) || globalEvents.hasKey(evnt, GLOBAL_EVENT_KEYS.SPACEBAR))) {
          $xeTable.ctxMenuLinkEvent(evnt, ctxMenuStore[property])
        }
      },
      handleOpenMenuEvent,
      /**
       * 快捷菜单事件处理
       */
      handleGlobalContextmenuEvent (evnt) {
        const $xeGrid = $xeTable.xeGrid
        const $xeGantt = $xeTable.xeGantt

        const { mouseConfig, menuConfig } = props
        const { editStore, ctxMenuStore } = reactData
        const { visibleColumn } = internalData
        const tableFilter = refTableFilter.value
        const tableMenu = refTableMenu.value
        const mouseOpts = computeMouseOpts.value
        const menuOpts = computeMenuOpts.value
        const el = refElem.value
        const { selected } = editStore
        const layoutList = ['header', 'body', 'footer']
        if (isEnableConf(menuConfig)) {
          if (ctxMenuStore.visible && tableMenu && getEventTargetNode(evnt, tableMenu.getRefMaps().refElem.value).flag) {
            evnt.preventDefault()
            return
          }
          if (internalData._keyCtx) {
            const type = 'body'
            const params: any = { source: 'table', type, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, keyboard: true, columns: visibleColumn.slice(0), $event: evnt }
            // 如果开启单元格区域
            if (mouseConfig && mouseOpts.area) {
              const activeArea = $xeTable.getActiveCellArea()
              if (activeArea && activeArea.row && activeArea.column) {
                params.row = activeArea.row
                params.column = activeArea.column
                handleOpenMenuEvent(evnt, type, params)
                return
              }
            } else if (mouseConfig && mouseOpts.selected) {
              // 如果启用键盘导航且已选中单元格
              if (selected.row && selected.column) {
                params.row = selected.row
                params.column = selected.column
                handleOpenMenuEvent(evnt, type, params)
                return
              }
            }
          }
          // 分别匹配表尾、内容、表尾的快捷菜单
          for (let index = 0; index < layoutList.length; index++) {
            const layout = layoutList[index] as 'header' | 'body' | 'footer'
            const columnTargetNode = getEventTargetNode(evnt, el, `vxe-${layout}--column`, (target: any) => {
              // target=td|th，直接向上找 table 去匹配即可
              return target.parentNode.parentNode.parentNode.getAttribute('xid') === xID
            })
            const params: any = { source: 'table', type: layout, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, columns: visibleColumn.slice(0), $event: evnt }
            if (columnTargetNode.flag) {
              const cell = columnTargetNode.targetElem
              const columnNodeRest = $xeTable.getColumnNode(cell)
              const column = columnNodeRest ? columnNodeRest.item : null
              let typePrefix = `${layout}-`
              if (column) {
                Object.assign(params, { column, columnIndex: $xeTable.getColumnIndex(column), cell })
              }
              if (layout === 'body') {
                const rowNodeRest = $xeTable.getRowNode(cell.parentNode)
                const row = rowNodeRest ? rowNodeRest.item : null
                typePrefix = ''
                if (row) {
                  params.row = row
                  params.rowIndex = $xeTable.getRowIndex(row)
                }
              }
              const eventType = `${typePrefix}cell-menu` as 'cell-menu' | 'header-cell-menu' | 'footer-cell-menu'
              handleOpenMenuEvent(evnt, layout, params)
              $xeTable.dispatchEvent(eventType, params, evnt)
              return
            } else if (getEventTargetNode(evnt, el, `vxe-table--${layout}-wrapper`, target => target.getAttribute('xid') === xID).flag || (layout === 'body' && getEventTargetNode(evnt, el, 'vxe-table--empty-place-wrapper', target => target.getAttribute('xid') === xID).flag)) {
              if (menuOpts.trigger === 'cell') {
                evnt.preventDefault()
              } else {
                handleOpenMenuEvent(evnt, layout, params)
              }
              return
            }
          }
        }
        if (tableFilter && !getEventTargetNode(evnt, tableFilter.getRefMaps().refElem.value).flag) {
          $xeTable.closeFilter()
        }
        menuMethods.closeMenu()
      },
      ctxMenuMouseoverEvent (evnt, item, child) {
        const menuElem = evnt.currentTarget as HTMLDivElement
        const { ctxMenuStore } = reactData
        evnt.preventDefault()
        evnt.stopPropagation()
        ctxMenuStore.selected = item
        ctxMenuStore.selectChild = child
        if (!child) {
          ctxMenuStore.showChild = hasChildrenList(item)
          if (ctxMenuStore.showChild) {
            nextTick(() => {
              const childWrapperElem = menuElem.nextElementSibling as HTMLDivElement
              if (childWrapperElem) {
                const { boundingTop, boundingLeft, visibleHeight, visibleWidth } = getAbsolutePos(menuElem)
                const posTop = boundingTop + menuElem.offsetHeight
                const posLeft = boundingLeft + menuElem.offsetWidth
                let left = ''
                let right = ''
                // 是否超出右侧
                if (posLeft + childWrapperElem.offsetWidth > visibleWidth - 10) {
                  left = 'auto'
                  right = `${menuElem.offsetWidth}px`
                }
                // 是否超出底部
                let top = ''
                let bottom = ''
                if (posTop + childWrapperElem.offsetHeight > visibleHeight - 10) {
                  top = 'auto'
                  bottom = '0'
                }
                childWrapperElem.style.left = left
                childWrapperElem.style.right = right
                childWrapperElem.style.top = top
                childWrapperElem.style.bottom = bottom
              }
            })
          }
        }
      },
      ctxMenuMouseoutEvent (evnt, item) {
        const { ctxMenuStore } = reactData
        if (!item.children) {
          ctxMenuStore.selected = null
        }
        ctxMenuStore.selectChild = null
      },
      /**
       * 快捷菜单点击事件
       */
      ctxMenuLinkEvent (evnt, menu) {
        const $xeGrid = $xeTable.xeGrid
        const $xeGantt = $xeTable.xeGantt

        // 如果一级菜单有配置 code 则允许点击，否则不能点击
        if (!menu.loading && !menu.disabled && (menu.code || !menu.children || !menu.children.length)) {
          const gMenuOpts = menus.get(menu.code)
          const params = Object.assign({}, internalData._currMenuParams, { menu, $table: $xeTable, $grid: $xeGrid, $gantt: $xeGantt, $event: evnt })
          const tmMethod = gMenuOpts ? (gMenuOpts.tableMenuMethod || gMenuOpts.menuMethod) : null
          if (tmMethod) {
            tmMethod(params, evnt)
          }
          $xeTable.dispatchEvent('menu-click', params, evnt)
          menuMethods.closeMenu()
        }
      }
    }

    return { ...menuMethods, ...menuPrivateMethods }
  },
  setupGrid ($xeGrid) {
    return $xeGrid.extendTableMethods(tableMenuMethodKeys)
  },
  setupGantt ($xeGantt) {
    return $xeGantt.extendTableMethods(tableMenuMethodKeys)
  }
})
