<script lang="ts" setup>
import { ElTree } from 'element-plus'
import { CaretLeft, CaretRight, Search } from '@element-plus/icons-vue'
import type { IotTreeVO } from '@/api/system/tree'
import { IotTreeApi } from '@/api/system/tree'
import { defaultProps, handleTree } from '@/utils/tree'

defineOptions({ name: 'IotTree' })

interface TreeNode extends IotTreeVO {
  children?: TreeNode[]
}

type TreeKey = number | string

const props = withDefaults(
  defineProps<{
    deviceId: number
    currentId?: TreeKey | null
  }>(),
  {
    currentId: null
  }
)

const emits = defineEmits<{
  (event: 'node-click', value: TreeNode): void
  (event: 'success', value?: number): void
}>()

const isCollapsed = ref(false)
const treeName = ref('')
const treeList = ref<TreeNode[]>([])
const treeRef = ref<InstanceType<typeof ElTree>>()
const expandedKeys = ref<Array<number | string>>([])

const getNodeIcon = (type: string) => {
  if (type === 'device') return 'fa:tasks'
  return 'fa:folder-open'
}

const getNodeIconClass = (type: string) => {
  return type === 'device' ? 'node-icon node-icon--device' : 'node-icon node-icon--folder'
}

const findNodePath = (
  nodes: TreeNode[],
  targetId: number | string,
  path: Array<number | string> = []
): Array<number | string> | null => {
  for (const node of nodes) {
    const nextPath = [...path, node.id]

    if (node.id === targetId) {
      return nextPath
    }

    if (node.children?.length) {
      const result = findNodePath(node.children, targetId, nextPath)
      if (result) return result
    }
  }

  return null
}

const locateNode = (targetId: number | string) => {
  if (!targetId || !treeList.value.length) return

  const pathIds = findNodePath(treeList.value, targetId)
  if (!pathIds) return

  expandedKeys.value = [...new Set([...expandedKeys.value, ...pathIds.slice(0, -1)])]

  nextTick(() => {
    treeRef.value?.setCurrentKey(targetId)
    treeRef.value?.getNode(targetId)?.$el?.scrollIntoView({
      behavior: 'smooth',
      block: 'nearest'
    })
  })
}

const getTreeInfo = async () => {
  const res = await IotTreeApi.getSimpleTreeList()
  treeList.value = handleTree(res) as TreeNode[]
  expandedKeys.value = treeList.value.map((node) => node.id)

  if (props.currentId) {
    locateNode(props.currentId)
  }

  emits('success', treeList.value[0]?.id)
}

const filterNode = (name: string, data: TreeNode) => {
  return !name || data.name.includes(name)
}

const handleNodeClick = (row: TreeNode) => {
  emits('node-click', row)
}

watch(treeName, (val) => {
  treeRef.value?.filter(val)
})

watch(
  () => props.currentId,
  (newVal) => {
    if (newVal) {
      locateNode(newVal)
    }
  }
)

onMounted(getTreeInfo)
</script>

<template>
  <div
    class="pms-tree-aside relative bg-white dark:bg-[#1d1e1f] shadow rounded-lg transition-all duration-300 ease-in-out overflow-visible"
    :class="[isCollapsed ? 'is-collapsed' : 'p-4']">
    <div v-show="!isCollapsed" class="h-full flex flex-col gap-4 overflow-hidden w-full">
      <div class="shrink-0">
        <el-input
          v-model="treeName"
          clearable
          placeholder="请输入名称"
          size="default"
          :prefix-icon="Search" />
      </div>

      <div class="tree-body">
        <el-auto-resizer class="absolute">
          <template #default="{ height }">
            <el-scrollbar :style="{ height: `${height}px` }">
              <el-tree
                ref="treeRef"
                :data="treeList"
                :default-expanded-keys="expandedKeys"
                :expand-on-click-node="false"
                :filter-node-method="filterNode"
                :props="defaultProps"
                highlight-current
                node-key="id"
                @node-click="handleNodeClick">
                <template #default="{ node }">
                  <div class="tree-node">
                    <Icon
                      :class="getNodeIconClass(node.data.type)"
                      :icon="getNodeIcon(node.data.type)" />
                    <span class="tree-node__label">{{ node.data.name }}</span>
                  </div>
                </template>
              </el-tree>
            </el-scrollbar>
          </template>
        </el-auto-resizer>
      </div>
    </div>

    <div class="collapse-handle" @click="isCollapsed = !isCollapsed">
      <el-icon size="12">
        <CaretLeft v-if="!isCollapsed" />
        <CaretRight v-else />
      </el-icon>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.pms-tree-aside {
  width: 100%;
  height: calc(
    100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
  );
  min-width: 240px;
  box-sizing: border-box;
  flex-shrink: 0;
}

.pms-tree-aside.is-collapsed {
  width: 0 !important;
  min-width: 0 !important;
  padding: 0 !important;
  overflow: visible !important;
  pointer-events: none;
  box-shadow: none;
}

:global(.left-tree:has(.pms-tree-aside.is-collapsed)) {
  width: 0 !important;
  overflow: visible !important;
  background: transparent !important;
}

:global(.left-tree:has(.pms-tree-aside.is-collapsed) + .divider-tree) {
  display: none;
}

.tree-body {
  position: relative;
  min-height: 0;
  overflow: hidden;
  flex: 1;
}

.tree-node {
  display: flex;
  width: 100%;
  min-width: 0;
  align-items: center;
}

.node-icon {
  margin-right: 6px;
  fill: currentcolor;
  flex-shrink: 0;
}

.node-icon--folder {
  color: #e6a23c;
}

.node-icon--device {
  color: #409eff;
}

.tree-node__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.collapse-handle {
  position: absolute;
  top: 50%;
  right: -14px;
  z-index: 200;
  display: flex;
  width: 14px;
  height: 60px;
  color: var(--el-text-color-secondary);
  pointer-events: auto;
  cursor: pointer;
  background-color: var(--el-bg-color);
  border: 1px solid var(--el-border-color-light);
  border-left: none;
  border-radius: 0 12px 12px 0;
  transform: translateY(-50%);
  box-shadow: 2px 0 6px rgb(0 0 0 / 5%);
  transition: right 0.3s;
  align-items: center;
  justify-content: center;
}

.is-collapsed .collapse-handle {
  right: -8px;
  border-left: 1px solid var(--el-border-color-light);
}

.collapse-handle:hover {
  color: var(--el-color-primary);
  background-color: var(--el-fill-color-light);
}

:deep(.el-tree) {
  --el-tree-node-hover-bg-color: var(--el-fill-color-light);

  background: transparent;
}

:deep(.el-tree-node__content) {
  height: 32px;
  border-radius: 4px;
}

:deep(.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content) {
  font-weight: 600;
  color: var(--el-color-primary);
  background-color: var(--el-color-primary-light-9);
}
</style>
