<template>
  <div>
    <ul>
      <template
        v-for="node in nestedTree"
        :key="node.id"
      >
        <TreeSelectNodes
          v-if="!node?.hidden"
          :node="node"
          :toggle-expand="toggleExpand"
          :toggle-checkbox="toggleCheckbox"
          :type="type"
          :disabled="isDisabled(node.id)"
        />
      </template>
    </ul>
  </div>
</template>

<script lang="ts" setup>
import { useTreeService } from '#adata-ui/components/elements/tree-select/TreeService.ts'
import TreeSelectNodes from './components/tree-select-nodes.vue'
import type { TreeData } from '#adata-ui/components/elements/tree-select/types'
import type { TreeNestedNode } from '#adata-ui/components/elements/tree-select/types'
import highlight from '#adata-ui/composables/highlight'
import { useDebounceFn } from '@vueuse/core'

interface Props {
  options: TreeData[]
  modelValue: (string | number)[]
  type?: 'checkbox' | 'select'
  isSearch?: boolean
  disabled?: boolean
  onlyOne?: boolean
  searchText?: string
  // eslint-disable-next-line vue/require-default-prop
  asyncSearch?: (keyword: string) => TreeData[]
  withoutId?: boolean
}

interface Emits {
  (e: 'update:modelValue', item: (string | number)[]): void
}

const emits = defineEmits<Emits>()
const props = withDefaults(defineProps<Props>(), {
  type: 'checkbox',
  searchText: '',
  withoutId: false,
  disabled: false,
  onlyOne: false,
})
const {
  nestedTree,
  tree,
  setState,
  setExpandableClosed,
  initTree,
  collectSelectedIds,
  updateNestedTree,
  searchedData,
  toggleHiddenNodes,
  reset,
  toggleExpandableNodes,
} = useTreeService( props.withoutId )
const selected = computed({
  get() {
    return props.modelValue
  },
  set(val) {
    emits('update:modelValue', val)
  }
})

initTree(props.options)
const toggleExpand = (node) => {
  if (node.expandable) {
    setExpandableClosed(node.id, !node.expandableClosed)
  }
}

const toggleCheckbox = (id, checked) => {
  setState(id, checked ? 'checked' : 'unchecked')
}

function openFounded(nodes: TreeNestedNode[]) {
  nodes.forEach((node) => {
    node.expandableClosed = false
    node.text = highlight(node.text, props.searchText)
    if (node.child.length) {
      openFounded(node.child)
    }
  })
}

function filterOptions(options: any[], search: string) {
  return options
    .map((option) => {
      const matches = option.name.toLowerCase().includes(search.toLowerCase())
      const matchesWithId = option.id?.toString().toLowerCase().includes(search.toLowerCase())
      let filteredChildren = []

      if (option.children) {
        filteredChildren = filterOptions(option.children, search)
      }

      if (matches || matchesWithId || filteredChildren.length > 0) {
        return {
          ...option,
          children: filteredChildren
        }
      }

      return null
    })
    .filter(Boolean)
}

const debouncedSearch = useDebounceFn(async () => {
  if (props.searchText !== '') {
    if (props?.asyncSearch) {
      const searchData = await props.asyncSearch(props.searchText)
      searchedData(searchData, props.searchText)
    } else {
      const searchData = filterOptions(props.options, props.searchText)
      searchedData(searchData, props.searchText)
    }
  } else {
    toggleHiddenNodes(false)
    toggleExpandableNodes(true)
    updateNestedTree()
  }
}, 300)

watch(
  () => props.searchText,
  (newVal) => debouncedSearch()
)


onMounted(() => {
  if (props.modelValue && props.modelValue.length) {
    props.modelValue.forEach((id) => {
      setState(id, 'checked')
    })
  }

  if (props.isSearch) {
    // openFounded(nestedTree.value)
  }
})

const isDisabled = (id: string | number) => {
  return props.onlyOne ? selected.value.length > 0 && !selected.value.includes(id) : false
}
watch(
  tree,
  () => {
    if (tree.value) {
      selected.value = collectSelectedIds(tree.value)
    }
  },
  { deep: true }
)
defineExpose({
  reset
})
</script>
