<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 { DataLibraryApi } from '@/api/pms/qhse/index'
import { defaultProps, handleTree } from '@/utils/tree'
import { CommonStatusEnum } from '@/utils/constants'

defineOptions({ name: 'IotTree' })

const formLoading = ref(false)
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formRef = ref() // 搜索的表单
const message = useMessage()
const nodeInfo = ref({})
const menuVisible = ref(false)
const menuX = ref(0)
const menuY = ref(0)
const contextMenuRef = ref(null) // 弹窗DOM引用
const { t } = useI18n()
let selectedNode = null
const parentId = ref()
const formType = ref<'create' | 'update'>('create')

const formData = ref({
  id: undefined,
  title: '',
  parentId: undefined,
  name: undefined,
  sort: undefined,
  leaderUserId: undefined,
  phone: undefined,
  email: undefined,
  status: CommonStatusEnum.ENABLE
})
const formRules = ref({
  name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
  sort: [{ required: true, message: '请输入排序', trigger: 'blur' }]
})
const handleRightClick = (event, data, node) => {
  nodeInfo.value = { ...data }
  event.preventDefault()
  const nodeContent = (event.target as HTMLElement)?.closest(
    '.el-tree-node__content'
  ) as HTMLElement | null
  if (nodeContent) {
    const rect = nodeContent.getBoundingClientRect()
    menuX.value = rect.right + 8
    menuY.value = rect.top
  } else {
    menuX.value = event.clientX
    menuY.value = event.clientY
  }
  selectedNode = data
  parentId.value = data.id

  menuVisible.value = true
}

const resetForm = () => {
  formData.value = {
    id: undefined,
    title: '',
    parentId: undefined,
    name: undefined,
    sort: undefined,
    leaderUserId: undefined,
    phone: undefined,
    email: undefined,
    status: CommonStatusEnum.ENABLE
  }
  formRef.value?.resetFields()
}

const handleMenuClick = async (action) => {
  switch (action) {
    case 'add':
      dialogVisible.value = true
      dialogTitle.value = '新增目录'
      formType.value = 'create'
      resetForm()
      break
    case 'edit':
      resetForm()
      dialogVisible.value = true
      dialogTitle.value = '编辑目录'
      formType.value = 'update'
      formData.value = { ...nodeInfo.value }

      break
    case 'delete':
      // 删除的二次确认
      await message.delConfirm()
      // 假设存在删除接口

      await DataLibraryApi.deleteFileTree(nodeInfo.value.id)

      message.success(t('common.delSuccess'))
      // 刷新列表
      await getTreeInfo()
      break
  }

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

const submitForm = async () => {
  // 表单验证逻辑
  formLoading.value = true
  try {
    const targetParentId = formType.value === 'create' ? parentId.value : formData.value.parentId
    formData.value.type = 'file'
    if (formType.value === 'create') {
      formData.value.parentId = parentId.value

      await DataLibraryApi.createFileTree(formData.value)
    } else {
      await DataLibraryApi.updateFileTree(formData.value)
    }
    message.success(t(formType.value === 'create' ? '添加成功' : '修改成功'))
    dialogVisible.value = false
    menuVisible.value = false
    await getTreeInfo()
    if (targetParentId) {
      expandedKeys.value = [...new Set([...expandedKeys.value, targetParentId])]
      locateNode(targetParentId)
    }
  } catch (error) {
    console.error(error)
  } finally {
    formLoading.value = false
  }
}

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 filterDeviceNodes = (nodes: TreeNode[]): TreeNode[] => {
  return nodes.reduce<TreeNode[]>((result, node) => {
    if (node.type === 'device') {
      return result
    }

    const nextNode: TreeNode = { ...node }
    if (node.children?.length) {
      nextNode.children = filterDeviceNodes(node.children)
    }

    result.push(nextNode)
    return result
  }, [])
}

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 DataLibraryApi.getFileTreeList()
  treeList.value = filterDeviceNodes(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)
    }
  }
)

const handleClickOutside = (event) => {
  if (menuVisible.value && contextMenuRef.value && !contextMenuRef.value.contains(event.target)) {
    menuVisible.value = false
  }
}

watch(menuVisible, (visible) => {
  if (visible) {
    document.addEventListener('click', handleClickOutside)
  } else {
    document.removeEventListener('click', handleClickOutside)
  }
})

onMounted(getTreeInfo)
onBeforeUnmount(() => {
  document.removeEventListener('click', handleClickOutside)
})
</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-contextmenu="handleRightClick"
                @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>

              <div
                v-show="menuVisible"
                ref="contextMenuRef"
                class="custom-menu"
                :style="{ left: menuX + 'px', top: menuY + 'px' }">
                <ul>
                  <li @click="handleMenuClick('add')" class="flex items-center gap-1">
                    <Icon icon="ep:plus" /> 新建目录</li
                  >

                  <li @click="handleMenuClick('edit')" class="flex items-center gap-1">
                    <Icon icon="ep:edit" />编辑目录</li
                  >

                  <li @click="handleMenuClick('delete')" class="flex items-center gap-1"
                    ><Icon icon="ep:delete" />删除目录</li
                  >
                </ul>
              </div>
            </el-scrollbar>
          </template>
        </el-auto-resizer>
      </div>
    </div>

    <Dialog v-model="dialogVisible" :title="dialogTitle" style="width: 40em">
      <el-form
        ref="formRef"
        v-loading="formLoading"
        :model="formData"
        :rules="formRules"
        label-width="80px">
        <div class="flex flex-col gap-4">
          <el-form-item label="目录名称" prop="name" label-width="110px">
            <el-input v-model="formData.name" placeholder="请输入目录名称" />
          </el-form-item>
          <el-form-item label="显示排序" prop="sort" label-width="110px">
            <el-input-number v-model="formData.sort" :min="0" controls-position="right" />
          </el-form-item>
        </div>
      </el-form>
      <template #footer>
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="dialogVisible = false">取 消</el-button>
      </template>
    </Dialog>

    <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;
}

.custom-menu {
  position: fixed;
  min-width: 144px;
  padding: 6px;
  background: #fff;
  border: 1px solid rgb(15 23 42 / 8%);
  border-radius: 10px;
  box-shadow: 0 14px 30px rgb(15 23 42 / 16%);
  z-index: 3000;
}

.custom-menu ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.custom-menu li {
  padding: 8px 12px;
  margin: 0;
  font-size: 13px;
  line-height: 1.2;
  color: #334155;
  cursor: pointer;
  border-radius: 8px;
  transition:
    background-color 0.18s ease,
    color 0.18s ease;
}

.custom-menu li:hover {
  color: #1677ff;
  background: #eff6ff;
}

.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>
