import warning from 'warning'
import PropTypes from '../../../_util/vue-types'
import { Tree } from '../../../vc-tree'
import BaseMixin from '../../../_util/BaseMixin'

// export const popupContextTypes = {
//   onPopupKeyDown: PropTypes.func.isRequired,
//   onTreeNodeSelect: PropTypes.func.isRequired,
//   onTreeNodeCheck: PropTypes.func.isRequired,
// }
function getDerivedStateFromProps (nextProps, prevState) {
  const { _prevProps: prevProps = {},
    _loadedKeys: loadedKeys,
    _expandedKeyList: expandedKeyList,
    _cachedExpandedKeyList: cachedExpandedKeyList,
  } = prevState || {}
  const {
    valueList, valueEntities, keyEntities,
    treeExpandedKeys, filteredTreeNodes, searchValue,
  } = nextProps

  const newState = {
    _prevProps: { ...nextProps },
  }

  // Check value update
  if (valueList !== prevProps.valueList) {
    newState._keyList = valueList
      .map(({ value }) => valueEntities[value])
      .filter(entity => entity)
      .map(({ key }) => key)
  }

  // Show all when tree is in filter mode
  if (
    !treeExpandedKeys &&
    filteredTreeNodes &&
    filteredTreeNodes.length &&
    filteredTreeNodes !== prevProps.filteredTreeNodes
  ) {
    newState._expandedKeyList = Object.keys(keyEntities)
  }

  // Cache `expandedKeyList` when filter set
  if (searchValue && !prevProps.searchValue) {
    newState._cachedExpandedKeyList = expandedKeyList
  } else if (!searchValue && prevProps.searchValue && !treeExpandedKeys) {
    newState._expandedKeyList = cachedExpandedKeyList || []
    newState._cachedExpandedKeyList = []
  }

  // Use expandedKeys if provided
  if (prevProps.treeExpandedKeys !== treeExpandedKeys) {
    newState._expandedKeyList = treeExpandedKeys
  }

  // Clean loadedKeys if key not exist in keyEntities anymore
  if (nextProps.loadData) {
    newState._loadedKeys = loadedKeys.filter(key => key in keyEntities)
  }

  return newState
}
const BasePopup = {
  mixins: [BaseMixin],
  name: 'BasePopup',
  props: {
    prefixCls: PropTypes.string,
    upperSearchValue: PropTypes.string,
    valueList: PropTypes.array,
    searchHalfCheckedKeys: PropTypes.array,
    valueEntities: PropTypes.object,
    keyEntities: PropTypes.object,
    treeIcon: PropTypes.bool,
    treeLine: PropTypes.bool,
    treeNodeFilterProp: PropTypes.string,
    treeCheckable: PropTypes.any,
    treeCheckStrictly: PropTypes.bool,
    treeDefaultExpandAll: PropTypes.bool,
    treeDefaultExpandedKeys: PropTypes.array,
    treeExpandedKeys: PropTypes.array,
    loadData: PropTypes.func,
    multiple: PropTypes.bool,
    // onTreeExpand: PropTypes.func,
    searchValue: PropTypes.string,
    treeNodes: PropTypes.any,
    filteredTreeNodes: PropTypes.any,
    notFoundContent: PropTypes.string,

    ariaId: PropTypes.string,
    switcherIcon: PropTypes.any,
    // HOC
    renderSearch: PropTypes.func,
    // onTreeExpanded: PropTypes.func,

    __propsSymbol__: PropTypes.any,
  },
  inject: {
    vcTreeSelect: { default: {}},
  },
  watch: {
    __propsSymbol__ () {
      const state = getDerivedStateFromProps(this.$props, this.$data)
      this.setState(state)
    },
  },
  data () {
    warning(this.$props.__propsSymbol__, 'must pass __propsSymbol__')
    const {
      treeDefaultExpandAll, treeDefaultExpandedKeys,
      keyEntities,
    } = this.$props

    // TODO: make `expandedKeyList` control
    let expandedKeyList = treeDefaultExpandedKeys
    if (treeDefaultExpandAll) {
      expandedKeyList = Object.keys(keyEntities)
    }

    const state = {
      _keyList: [],
      _expandedKeyList: expandedKeyList,
      // Cache `expandedKeyList` when tree is in filter. This is used in `getDerivedStateFromProps`
      _cachedExpandedKeyList: [], // eslint-disable-line react/no-unused-state
      _loadedKeys: [],
      _prevProps: {},
    }
    return {
      ...state,
      ...getDerivedStateFromProps(this.$props, state),
    }
  },
  methods: {
    onTreeExpand  (expandedKeyList) {
      const { treeExpandedKeys } = this.$props

      // Set uncontrolled state
      if (!treeExpandedKeys) {
        this.setState({ _expandedKeyList: expandedKeyList }, () => {
          this.__emit('treeExpanded')
        })
      }
      this.__emit('treeExpand', expandedKeyList)
    },

    onLoad  (loadedKeys) {
      this.setState({ _loadedKeys: loadedKeys })
    },

    /**
     * Not pass `loadData` when searching. To avoid loop ajax call makes browser crash.
     */
    getLoadData  () {
      const { loadData, searchValue } = this.$props
      if (searchValue) return null
      return loadData
    },

    /**
     * This method pass to Tree component which is used for add filtered class
     * in TreeNode > li
     */
    filterTreeNode  (treeNode) {
      const { upperSearchValue, treeNodeFilterProp } = this.$props

      const filterVal = treeNode[treeNodeFilterProp]
      if (typeof filterVal === 'string') {
        return upperSearchValue && (filterVal).toUpperCase().indexOf(upperSearchValue) !== -1
      }

      return false
    },

    renderNotFound  () {
      const { prefixCls, notFoundContent } = this.$props

      return (
        <span class={`${prefixCls}-not-found`}>
          {notFoundContent}
        </span>
      )
    },
  },

  render () {
    const { _keyList: keyList, _expandedKeyList: expandedKeyList, _loadedKeys: loadedKeys } = this.$data
    const {
      prefixCls,
      treeNodes, filteredTreeNodes,
      treeIcon, treeLine, treeCheckable, treeCheckStrictly, multiple,
      ariaId,
      renderSearch,
      switcherIcon,
      searchHalfCheckedKeys,
    } = this.$props
    const { vcTreeSelect: {
      onPopupKeyDown,
      onTreeNodeSelect,
      onTreeNodeCheck,
    }} = this

    const loadData = this.getLoadData()

    const treeProps = {}

    if (treeCheckable) {
      treeProps.checkedKeys = keyList
    } else {
      treeProps.selectedKeys = keyList
    }
    let $notFound
    let $treeNodes
    if (filteredTreeNodes) {
      if (filteredTreeNodes.length) {
        treeProps.checkStrictly = true
        $treeNodes = filteredTreeNodes

        // Fill halfCheckedKeys
        if (treeCheckable && !treeCheckStrictly) {
          treeProps.checkedKeys = {
            checked: keyList,
            halfChecked: searchHalfCheckedKeys,
          }
        }
      } else {
        $notFound = this.renderNotFound()
      }
    } else if (!treeNodes.length) {
      $notFound = this.renderNotFound()
    } else {
      $treeNodes = treeNodes
    }

    let $tree
    if ($notFound) {
      $tree = $notFound
    } else {
      const treeAllProps = {
        props: {
          prefixCls: `${prefixCls}-tree`,
          showIcon: treeIcon,
          showLine: treeLine,
          selectable: !treeCheckable,
          checkable: treeCheckable,
          checkStrictly: treeCheckStrictly,
          multiple: multiple,
          loadData: loadData,
          loadedKeys: loadedKeys,
          expandedKeys: expandedKeyList,
          filterTreeNode: this.filterTreeNode,
          switcherIcon: switcherIcon,
          ...treeProps,
          __propsSymbol__: Symbol(),
          children: $treeNodes,
        },
        on: {
          select: onTreeNodeSelect,
          check: onTreeNodeCheck,
          expand: this.onTreeExpand,
          load: this.onLoad,
        },
      }
      $tree = (
        <Tree
          {...treeAllProps}
        />
      )
    }

    return (
      <div
        role='listbox'
        id={ariaId}
        onKeydown={onPopupKeyDown}
        tabIndex={-1}
      >
        {renderSearch ? renderSearch() : null}
        {$tree}
      </div>
    )
  },
}

export default BasePopup
