<template>
  <el-popover
      popper-class="tree-selector"
      ref="popover"
      placement="bottom-start"
      trigger="click"
      width="auto"
      :disabled="disabled"
      @show="showTree = true"
      @hide="showTree = false">
    <div class="popover-panel" v-if="showTree" :style="popoverPanelStyle">
      <el-input type="text" name="query" v-model="query" v-if="!lazyTree" :placeholder="queryPlaceHolder"
                class="query-input">
        <template #prefix>
          <FontAwesomeIcon :icon="['fas','search']"></FontAwesomeIcon>
        </template>
      </el-input>
      <el-tree
          class="tree-panel"
          :filter-node-method="filterMethod"
          :props="treeProps"
          :lazy="lazyTree"
          :load="loadNode"
          :node-key="nodeKey"
          :show-checkbox="multiple"
          :data="data"
          :expand-on-click-node="false"
          :default-expanded-keys="defaultExpandedKeys"
          :default-checked-keys="modelValue instanceof Array ? modelValue.map(v => v[nodeKey]) : [modelValue ? modelValue[nodeKey]: null]"
          @node-click="nodeClickHandler"
          @check="nodeCheckHandler"
          @current-change="currentNodeChange"
          @node-expand="nodeExpandHandler"
          ref="treeRef"></el-tree>
    </div>
    <template #reference>
      <div class="el-select">
        <div
            class="el-select__tags"
            v-if="multiple"
            ref="tags">
        <span v-if="modelValue && modelValue.length">
          <el-tag
              :closable="false"
              :hit="modelValue[0].hitState"
              type="info"
              size="small"
              disable-transitions>
            <span class="el-select__tags-text">{{ getNodeLabel(modelValue[0]) }}</span>
          </el-tag>
          <el-tag
              v-if="modelValue.length > 1"
              :closable="false"
              size="small"
              type="info"
              disable-transitions>
            <span class="el-select__tags-text">+ {{ modelValue.length - 1 }}</span>
          </el-tag>
        </span>
        </div>
        <el-input
            ref="select"
            :disabled="disabled"
            :placeholder="placeholder"
            :readonly="true"
            :model-value="inputValue">
          <template #suffix>
            <font-awesome-icon v-if="showClose" :icon="['fas', 'times-circle']" class="el-select__caret" @click.stop="handleClearClick" />
            <font-awesome-icon v-if="!showClose" :icon="['fas', 'chevron-down']" class="el-select__caret" :class="iconClass" />
          </template>
        </el-input>
      </div>
    </template>
  </el-popover>
</template>

<script setup>
// 引入需要的组件和API
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { ref, computed, watch, nextTick, onMounted, onUpdated, useAttrs } from 'vue'

// 定义props
const props = defineProps({
  filterMethod: Function,
  multiple: {
    type: Boolean,
    default: false
  },
  lazyTree: {
    type: Boolean,
    default: false
  },
  loadNode: {
    type: Function,
    default: () => {}
  },
  nodeKey: {
    type: String,
    default: (props) => {
      return props.valueProp && typeof props.valueProp === 'string' ? props.valueProp : ''
    }
  },
  treeProps: {
    type: Object,
    default: () => ({})
  },
  data: {
    type: Array,
    default: () => []
  },
  defaultExpandedKeys: {
    type: Array,
    default: () => []
  },
  disabled: {
    type: Boolean,
    default: false
  },
  placeholder: {
    type: String,
    default: ''
  },
  queryPlaceHolder: {
    type: String,
    default: '请输入筛选内容'
  },
  modelValue: {
    type: [Array, String, Number, Object],
    default: () => []
  },
  valueProp: {
    type: [Object, String],
    default: null
  },
  selectionFilter: {
    type: Function,
    default: (data, node, tree) => true
  }
})

// 定义emit
const emit = defineEmits(['update:modelValue'])

// 获取attrs
const attrs = useAttrs()

// 定义响应式数据
const popoverPanelStyle = ref({})
const query = ref(null)
const showTree = ref(false)
const showClose = ref(false)
const popShow = ref(false)

// 定义refs
const popover = ref(null)
const treeRef = ref(null)
const select = ref(null)
const tags = ref(null)

// 计算属性
const iconClass = computed(() => {
  return (showTree.value ? 'is-reverse' : '')
})

// 获取节点标签文本
const getNodeLabel = (node) => {
  if (!node) return '';

  // 如果 treeProps.label 是函数，则调用该函数获取标签文本
  if (typeof props.treeProps.label === 'function') {
    return props.treeProps.label(node);
  }

  // 如果 treeProps.label 是字符串，则直接获取对应属性
  return node[props.treeProps.label] || '';
};

const inputValue = computed(() => {
  let value
  let nodeKey
  if (!props.multiple) {
    if (props.modelValue) {
      if (props.modelValue instanceof Object) {
        // 使用 getNodeLabel 获取标签文本
        value = getNodeLabel(props.modelValue);
        if (!value) {
          nodeKey = props.modelValue[props.nodeKey]
        }
      } else {
        nodeKey = props.modelValue
      }

      if (!value) {
        const valueObj = findFirstTreeNodeData(props.data, data => data[props.nodeKey] === nodeKey)
        value = valueObj ? getNodeLabel(valueObj) : null
      }
    }
  }
  toggleShowClose(value)
  return value
})

// 监听query变化
watch(query, (val) => {
  treeRef.value.filter(val)
})

// 方法
const findFirstTreeNodeData = (treeData, queryPredicate) => {
  // 树形节点里筛选符合表达式的数据
  const findData = treeData.find(queryPredicate)
  if (!findData) {
    return treeData.map(td => td[props.treeProps.children] || []).reduce((result, next) => !result ? findFirstTreeNodeData(next, queryPredicate) : result, null)
  }
  return findData
}

const nodeClickHandler = (data, node, tree) => {
  if (!props.multiple) {
    if (selection(data, node, tree)) {
      closeSelectWindow()
    }
  } else {
    // 多选模式下，点击节点时切换复选框状态
    treeRef.value.setChecked(node, !node.checked, true)
    // 获取当前所有选中的节点
    const checkedNodes = treeRef.value.getCheckedNodes()
    // 更新选中值
    selection(checkedNodes)
  }
}

const nodeExpandHandler = (data, node, self) => {
  // 节点展开处理
}

const nodeCheckHandler = (data, { checkedKeys, checkedNodes, halfCheckedKeys, halfCheckedNodes }) => {
  selection(checkedNodes)
}

const currentNodeChange = (data, node) => {
  // 当前节点变化处理
  console.log(data, node);

}

const selection = (data, node, tree) => {
  const result = props.selectionFilter(data, node, tree)
  if (result) {
    updateModelValue(data)
  }
  return result
}

const updateModelValue = (val) => {
  const value = val instanceof Array ? val.map(computedValue) : computedValue(val)
  emit('update:modelValue', value)
  toggleShowClose(value)
}

const computedValue = (data) => {
  if (props.valueProp) {
    if (props.valueProp instanceof Object) {
      return Object.fromEntries(Object.entries(props.valueProp).map(([valueProperty, dataProperty]) => {
        return [valueProperty, data[dataProperty]]
      }))
    } else {
      return data[props.valueProp]
    }
  } else {
    return data
  }
}

const closeSelectWindow = () => {
  popover.value.hide()
}

const handleClearClick = () => {
  if (props.disabled) {
    return false
  }
  showTree.value = false
  // 在更新模型值之前清空树的选中状态
  if (treeRef.value) {
    treeRef.value.setCheckedKeys([])
  }
  if (props.multiple) {
    updateModelValue([])
  } else {
    updateModelValue('')
  }
}

const toggleShowClose = (val) => {
  showClose.value = props.multiple ? val?.length > 0 : !!val
}

const expandDefaultKeys = () => {
  if (!treeRef.value || !props.defaultExpandedKeys || !props.defaultExpandedKeys.length) return
  props.defaultExpandedKeys.forEach((key) => {
    const node = treeRef.value.getNode(key)
    if (node && !node.expanded) {
      if (typeof node.expand === 'function') {
        node.expand()
      } else {
        node.expanded = true
      }
    }
  })
}

watch(() => showTree.value, async (v) => {
  if (v) {
    await nextTick()
    expandDefaultKeys()
  }
})

watch(() => props.data, async () => {
  if (showTree.value) {
    await nextTick()
    expandDefaultKeys()
  }
})

// 生命周期钩子
onMounted(() => {
  nextTick(() => {
    popoverPanelStyle.value = {
      'min-width': select.value.$el.clientWidth + 'px'
    }
  })
})

onUpdated(() => {
  if (showTree.value) {
    if (props.modelValue && props.multiple) {
      treeRef.value.setCheckedKeys([...props.modelValue].map(n => n[props.nodeKey]))
    }
  }
})
</script>
<style lang="scss">
.tree-selector {
  .tree-panel {
    max-height: 300px;
    overflow: auto;
  }
  .query-input {
    margin-bottom: 4px;
  }
}
</style>
