<script lang="ts" setup>
import {
  ref,
  PropType,
  toRefs,
  nextTick,
  watch,
  onMounted,
  computed,
  inject,
  Ref,
  onBeforeMount,
  shallowRef
} from 'vue'
import { useEmitt } from '@/hooks/web/useEmitt'
import { cloneDeep, debounce, sortBy } from 'lodash-es'
import { getFieldTree } from '@/api/dataset'
import colorFunctions from 'less/lib/less/functions/color.js'
import colorTree from 'less/lib/less/tree/color.js'
import { colorStringToHex } from '@/utils/color'
import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()

interface SelectConfig {
  selectValue: any
  required: false
  defaultMapValue: any
  defaultValue: any
  queryConditionWidth: number
  resultMode: number
  checkedFieldsMap: object
  displayType: string
  id: string
  placeholder: string
  checkedFields: string[]
  treeFieldList: Array<any>
  dataset: {
    id: string
  }
  field: {
    id: string
  }
  defaultValueCheck: boolean
  multiple: boolean
  optionFilter: []
}

const customStyle: any = inject('$custom-style-filter')
const cascadeList = inject('cascade-list', Function, true)
const props = defineProps({
  config: {
    type: Object as PropType<SelectConfig>,
    default: () => {
      return {
        selectValue: '',
        defaultValue: '',
        required: false,
        queryConditionWidth: 0,
        displayType: '',
        resultMode: 0,
        defaultValueCheck: false,
        multiple: false,
        checkedFieldsMap: {},
        treeFieldList: [],
        optionFilter: []
      }
    }
  },
  isConfig: {
    type: Boolean,
    default: false
  }
})

const placeholder: Ref = inject('placeholder')
const placeholderText = computed(() => {
  if (placeholder?.value?.placeholderShow) {
    return ['', undefined].includes(props.config.placeholder) ? ' ' : props.config.placeholder
  }
  return ' '
})
const { config } = toRefs(props)
const fromTreeSelectConfirm = ref(false)
const multiple = ref(false)
const treeSelectConfirm = val => {
  treeValue.value = val
  handleValueChange()
}

const handleValueChange = () => {
  fromTreeSelectConfirm.value = true
  const value = Array.isArray(treeValue.value) ? [...treeValue.value] : treeValue.value
  if (!props.isConfig) {
    config.value.selectValue = Array.isArray(treeValue.value)
      ? [...treeValue.value]
      : treeValue.value
    nextTick(() => {
      fromTreeSelectConfirm.value = false
      isConfirmSearch(config.value.id)
    })
    return
  }
  config.value.defaultValue = value
  fromTreeSelectConfirm.value = false
}

const changeFromId = ref(false)
watch(
  () => config.value.id,
  () => {
    changeFromId.value = true
    init()
    nextTick(() => {
      changeFromId.value = false
    })
  }
)
let oldId
watch(
  () => config.value.treeFieldList,
  val => {
    let idStr = val.map(ele => ele.id).join('-')
    if (changeFromId.value || idStr === oldId) return
    oldId = idStr
    treeValue.value = config.value.multiple ? [] : undefined
    config.value.defaultValue = config.value.multiple ? [] : undefined
    config.value.selectValue = config.value.multiple ? [] : undefined
    showOrHide.value = false
    getTreeOption()
  }
)

const init = (fromMount = false) => {
  loading.value = true
  const { defaultValueCheck, multiple: plus, defaultValue } = config.value
  if (defaultValueCheck) {
    config.value.selectValue = Array.isArray(defaultValue)
      ? cloneDeep([...defaultValue])
      : defaultValue
    treeValue.value = Array.isArray(defaultValue) ? cloneDeep([...defaultValue]) : defaultValue
  } else {
    config.value.selectValue = plus ? [] : undefined
    treeValue.value = plus ? [] : undefined
  }
  nextTick(() => {
    oldId = config.value.treeFieldList?.map(ele => ele.id).join('-')
    multiple.value = config.value.multiple
  })
  if (getCascadeFieldId().some(ele => ele.defaultValueFirstItem) && fromMount && !props.isConfig)
    return
  getTreeOption()
}

const tagWidth = computed(() => {
  return Math.min(getCustomWidth() / 3, 50) + 'px'
})

const tagsWidth = computed(() => {
  return getCustomWidth() - 40 + 'px'
})

const tagTextWidth = computed(() => {
  return Math.min(getCustomWidth() / 3, 50) - 25 + 'px'
})

const showOrHide = ref(true)
const queryConditionWidth = inject('com-width', Function, true)
const isConfirmSearch = inject('is-confirm-search', Function, true)
const isConfirmSearchNoRequiredName = inject('query-data-for-id-tree', Function, true)
watch(
  () => config.value.id,
  () => {
    getTreeOption()
  }
)
onMounted(() => {
  setTimeout(() => {
    fromSelect = true
    init(true)
  }, 0)
})

watch(
  () => config.value.defaultValue,
  val => {
    if (props.isConfig) return
    if (config.value.multiple) {
      treeValue.value = Array.isArray(val) ? [...val] : val
    }
    nextTick(() => {
      multiple.value = config.value.multiple
    })
  }
)

watch(
  () => config.value.selectValue,
  val => {
    if (props.isConfig || fromTreeSelectConfirm.value) return

    if (config.value.multiple) {
      treeValue.value = Array.isArray(val) ? [...val] : val
    }

    nextTick(() => {
      multiple.value = config.value.multiple
      if (!config.value.multiple) {
        treeValue.value = Array.isArray(config.value.selectValue)
          ? [...config.value.selectValue]
          : config.value.selectValue
      }
    })
  }
)

const showWholePath = ref(false)
watch(
  () => config.value.multiple,
  val => {
    if (!props.isConfig || changeFromId.value) return
    showWholePath.value = false
    if (val) {
      treeValue.value = []
    }
    nextTick(() => {
      multiple.value = val
      if (!val) {
        nextTick(() => {
          treeValue.value = undefined
        })
      }
      nextTick(() => {
        showWholePath.value = true
      })
    })
  }
)
let cacheId = ''
let treeOptionList = shallowRef([])
const filterMethod = (value, data) =>
  (data.label ?? '').toLowerCase().includes((value ?? '').toLowerCase())

const dfs = arr => {
  const mapped = (arr || []).map(ele => {
    const label = ele.text
    const children = ele.children?.length ? dfs(ele.children) : []
    return { ...ele, value: ele.id, label, children }
  })
  return sortBy(mapped, node => (node.label ?? '').toLowerCase())
}
const cascade = computed(() => {
  return cascadeList() || []
})
const loading = ref(false)

const getCascadeFieldId = () => {
  const filter = []
  cascade.value.forEach(ele => {
    let condition = null
    ele.forEach(item => {
      const [_, queryId, fieldId] = item.datasetId.split('--')
      const defaultValueFirstItem = item.defaultValueFirstItem
      if (queryId === config.value.id && condition) {
        if (item.fieldId) {
          condition.fieldId = item.fieldId
        }
        filter.push(condition)
      } else {
        if (props.isConfig) {
          if (!!item.selectValue?.length) {
            condition = {
              fieldId,
              defaultValueFirstItem,
              operator: 'in',
              value: [...item.selectValue]
            }
          }
        } else {
          if (!!item.currentSelectValue?.length) {
            condition = {
              fieldId,
              defaultValueFirstItem,
              operator: 'in',
              value: [...item.currentSelectValue]
            }
          }
        }
      }
    })
  })
  return filter
}

let fromSelect = false
const getOptionFromCascade = () => {
  fromSelect = true
  getTreeOption()
}

onBeforeMount(() => {
  useEmitt({
    name: `${config.value.id}-select`,
    callback: getOptionFromCascade
  })
})

const dfsAuth = (tree, val) => {
  return tree.some(ele => {
    if (ele.value === val) {
      return true
    }

    if (ele.children?.length) {
      return dfsAuth(ele.children, val)
    }

    return false
  })
}

function containsNodeById(source, params) {
  // 统一处理参数为数组
  const searchIds = Array.isArray(params) ? params : [params]

  // 递归搜索函数
  function searchById(node) {
    // 检查当前节点的id是否在搜索列表中
    if (searchIds.includes(node.id)) {
      return true
    }

    // 递归搜索子节点
    if (node.children && node.children.length > 0) {
      for (const child of node.children) {
        if (searchById(child)) {
          return true
        }
      }
    }

    return false
  }

  // 遍历所有根节点
  for (const node of source) {
    if (searchById(node)) {
      return true
    }
  }

  return false
}

const getTreeOption = debounce(() => {
  loading.value = true
  getFieldTree({
    fieldIds: props.config.treeFieldList.map(ele => ele.id),
    resultMode: config.value.resultMode || 0,
    filter: getCascadeFieldId()
  })
    .then(res => {
      treeOptionList.value = filterTree(dfs(res), config.value.optionFilter)
      if (config.value?.required && config.value?.optionFilter?.length > 0) {
        const isValid = containsNodeById(treeOptionList.value, config.value.selectValue)
        if (!isValid) {
          config.value.selectValue = null
          ElMessage({
            message: `【${config.value?.name}】${t('v_query.before_querying')}`,
            type: 'error',
            duration: 3000
          })
        }
      }

      if (fromSelect) {
        fromTreeSelectConfirm.value = true
        if (multiple.value && Array.isArray(treeValue.value) && treeValue.value.length) {
          treeValue.value = treeValue.value.filter(ele => dfsAuth(treeOptionList.value, ele))
        } else if (treeValue.value && !dfsAuth(treeOptionList.value, treeValue.value)) {
          treeValue.value = undefined
        } else {
          fromSelect = false
          fromTreeSelectConfirm.value = false
        }

        if (fromSelect) {
          config.value.selectValue = Array.isArray(treeValue.value)
            ? [...treeValue.value]
            : treeValue.value
          config.value.defaultValue = config.value.selectValue

          if (props.isConfig) return

          nextTick(() => {
            fromTreeSelectConfirm.value = false
            isConfirmSearchNoRequiredName(config.value.id)
          })
        }
      }
    })
    .finally(() => {
      loading.value = false
      showWholePath.value = true
      fromSelect = false
    })
}, 300)
watch(
  () => props.config.treeFieldList,
  val => {
    if (!props.isConfig) return
    const ids = val.map(ele => ele.id).join('')
    if (cacheId !== val.map(ele => ele.id).join('')) {
      cacheId = ids
      getTreeOption()
    }
  }
)
const fakeValue = ref('')
const treeValue = ref()
const getCustomWidth = () => {
  if (placeholder?.value?.placeholderShow) {
    if (props.config.queryConditionWidth !== undefined) {
      return props.config.queryConditionWidth
    }
    return queryConditionWidth()
  }
  return 227
}
const selectStyle = computed(() => {
  return props.isConfig ? {} : { width: getCustomWidth() + 'px' }
})

const tagColor = computed(() => {
  if (
    !customStyle ||
    ['#FFFFFF', 'rgba(255, 255, 255, 1)', 'rgb(255, 255, 255)'].includes(customStyle.background)
  )
    return ''
  if (customStyle.background === '#131C42') return 'rgb(38, 53, 82)'
  const hexColor = customStyle.background.startsWith('#')
    ? customStyle.background
    : colorStringToHex(customStyle.background)

  return colorFunctions
    .mix(new colorTree('ffffff'), new colorTree(hexColor.substr(1)), { value: 20 })
    .toRGB()
})

function filterTree(treeData, filterIds) {
  if (!filterIds || filterIds.length === 0) {
    return treeData
  }
  const filterSet = new Set(filterIds)

  // 用于存储最终保留的所有节点ID
  const keepIds = new Set()

  // 用于查找节点的Map
  const nodeMap = new Map()
  // 用于构建节点关系的Map(子节点到父节点)
  const parentMap = new Map()

  // 遍历所有节点,构建Map和父子关系
  function traverse(nodes, parentId = null) {
    for (const node of nodes) {
      nodeMap.set(node.id, node)
      if (parentId) {
        parentMap.set(node.id, parentId)
      }

      // 递归处理子节点
      if (node.children && node.children.length > 0) {
        traverse(node.children, node.id)
      }
    }
  }

  // 收集所有匹配的节点及其祖先和后代
  function collectRelatedNodes(nodeId) {
    if (keepIds.has(nodeId)) return

    keepIds.add(nodeId)
    const node = nodeMap.get(nodeId)

    // 1. 收集所有祖先节点
    let currentId = nodeId
    while (parentMap.has(currentId)) {
      const parentId = parentMap.get(currentId)
      keepIds.add(parentId)
      currentId = parentId
    }

    // 2. 收集所有后代节点(递归)
    function collectDescendants(node) {
      if (node.children && node.children.length > 0) {
        for (const child of node.children) {
          keepIds.add(child.id)
          collectDescendants(child)
        }
      }
    }
    collectDescendants(node)
  }

  // 第二步:递归构建过滤后的树
  function buildFilteredTree(nodes) {
    const result = []

    for (const node of nodes) {
      // 如果节点ID在保留集合中,则保留该节点
      if (keepIds.has(node.id)) {
        const newNode = { ...node }

        // 递归处理子节点
        if (newNode.children && newNode.children.length > 0) {
          newNode.children = buildFilteredTree(newNode.children)
        }

        result.push(newNode)
      }
    }

    return result
  }

  // 执行遍历和构建
  traverse(treeData)

  for (const filterId of filterIds) {
    if (nodeMap.has(filterId)) {
      collectRelatedNodes(filterId)
    }
  }

  return buildFilteredTree(treeData)
}
</script>

<template>
  <el-tree-select
    v-model="treeValue"
    :data="treeOptionList"
    clearable
    v-if="multiple && !loading"
    @treeSelectConfirm="treeSelectConfirm"
    :render-after-expand="false"
    show-checkbox
    showBtn
    @change="handleValueChange"
    :placeholder="placeholderText"
    collapse-tags
    :filter-node-method="filterMethod"
    :showWholePath="showWholePath"
    collapse-tags-tooltip
    :tagColor="tagColor"
    :key="'multipleTree' + getCustomWidth()"
    filterable
    :style="selectStyle"
    multiple
  />
  <el-tree-select
    v-model="treeValue"
    @change="handleValueChange"
    :data="treeOptionList"
    check-strictly
    clearable
    :filter-node-method="filterMethod"
    :placeholder="placeholderText"
    :render-after-expand="false"
    v-else-if="!multiple && !loading"
    :key="'singleTree' + getCustomWidth()"
    :showWholePath="showWholePath"
    :style="selectStyle"
    filterable
  />
  <el-tree-select
    v-model="fakeValue"
    v-loading="loading"
    :data="[]"
    :placeholder="placeholderText"
    :render-after-expand="false"
    v-else
    key="fakeTree"
    :style="selectStyle"
  />
</template>

<style lang="less" scoped>
:deep(.ed-select-tags-wrapper) {
  display: inline-flex !important;
}

:deep(.ed-select__tags) {
  max-width: v-bind(tagsWidth) !important;
  .ed-tag {
    max-width: v-bind(tagWidth);
  }

  .ed-select__tags-text {
    max-width: v-bind(tagTextWidth) !important;
  }
}
</style>