<template>
  <a-scrollbar>
    <!-- 分组树 -->
    <a-tree v-if="treeData.length"
            ref="tree"
            class="tree-container"
            :blockNode="true"
            :draggable="editable"
            :data="treeData"
            :checkable="checkable"
            v-model:checked-keys="checkedKeys"
            :check-strictly="true"
            @drop="moveGroup">
      <!-- 标题 -->
      <template #title="node">
        <!-- 修改名称输入框 -->
        <template v-if="node.editable">
          <a-input size="mini"
                   ref="renameInput"
                   v-model="currName"
                   style="width: 138px;"
                   placeholder="名称"
                   :max-length="32"
                   :disabled="node.loading"
                   @blur="() => saveNode(node.key)"
                   @pressEnter="() => saveNode(node.key)"
                   @change="() => saveNode(node.key)">
            <template #suffix>
              <!-- 加载中 -->
              <icon-loading v-if="node.loading" />
              <!-- 保存 -->
              <icon-check v-else
                          class="pointer"
                          title="保存"
                          @click="() => saveNode(node.key)" />
            </template>
          </a-input>
        </template>
        <!-- 名称 -->
        <span v-else
              class="node-title-wrapper"
              @click="() => emits('selectNode', node)">
        {{ node.title }}
      </span>
      </template>
      <!-- 操作图标 -->
      <template #drag-icon="{ node }">
        <a-space v-if="!node.editable">
          <!-- 重命名 -->
          <span v-permission="['asset:host-group:update']"
                class="tree-icon"
                title="重命名"
                @click="rename(node.title, node.key)">
          <icon-edit />
        </span>
          <!-- 删除 -->
          <a-popconfirm content="确认删除这条记录吗?"
                        position="left"
                        type="warning"
                        @ok="deleteNode(node.key)">
          <span v-permission="['asset:host-group:delete']"
                class="tree-icon" title="删除">
            <icon-delete />
          </span>
          </a-popconfirm>
          <!-- 新增 -->
          <span v-permission="['asset:host-group:create']"
                class="tree-icon"
                title="新增"
                @click="addChildren(node)">
          <icon-plus />
        </span>
        </a-space>
      </template>
    </a-tree>
    <!-- 无数据 -->
    <div v-else-if="!loading" class="empty-container">
      <span>暂无数据</span>
      <span v-if="editable">点击上方 '<icon-plus />' 添加一个分组吧~</span>
    </div>
  </a-scrollbar>
</template>

<script lang="ts">
  export default {
    name: 'host-group-tree'
  };
</script>

<script lang="ts" setup>
  import type { TreeNodeData } from '@arco-design/web-vue';
  import { computed, nextTick, onMounted, ref } from 'vue';
  import { createGroupGroupPrefix, rootId } from './types/const';
  import { findNode, findParentNode, moveNode } from '@/utils/tree';
  import { createHostGroup, deleteHostGroup, updateHostGroupName, moveHostGroup } from '@/api/asset/host-group';
  import { isString } from '@/utils/is';
  import { useCacheStore } from '@/store';

  const props = defineProps({
    loading: Boolean,
    editable: {
      type: Boolean,
      default: true
    },
    checkable: {
      type: Boolean,
      default: false
    },
    checkedKeys: {
      type: Array<Number>,
      default: () => []
    }
  });
  const emits = defineEmits(['loading', 'selectNode', 'update:checkedKeys']);

  const cacheStore = useCacheStore();

  const tree = ref();
  const modCount = ref(0);
  const renameInput = ref();
  const currName = ref();
  const treeData = ref<Array<TreeNodeData>>([]);

  const checkedKeys = computed<Array<number>>({
    get() {
      return props.checkedKeys as Array<number>;
    },
    set(e) {
      if (e) {
        emits('update:checkedKeys', e);
      } else {
        emits('update:checkedKeys', []);
      }
    }
  });

  // 重命名
  const rename = (title: number, key: number) => {
    const node = findNode<TreeNodeData>(key, treeData.value);
    if (!node) {
      return;
    }
    currName.value = title;
    node.editable = true;
    nextTick(() => {
      renameInput.value?.focus();
    });
  };

  // 删除节点
  const deleteNode = async (key: number) => {
    try {
      emits('loading', true);
      // 删除
      await deleteHostGroup(key);
      // 页面删除
      const parentNode = findParentNode<TreeNodeData>(key, treeData.value);
      if (!parentNode) {
        return;
      }
      const children = parentNode.root ? treeData.value : parentNode.children;
      if (children) {
        // 删除
        for (let i = 0; i < children.length; i++) {
          if (children[i].key === key) {
            children.splice(i, 1);
            break;
          }
        }
      }
    } catch (e) {
    } finally {
      emits('loading', false);
    }
  };

  // 新增根节点
  const addRootNode = () => {
    const newKey = `${createGroupGroupPrefix}${Date.now()}`;
    treeData.value.push({
      title: '新分组',
      key: newKey
    });
    // 编辑子节点
    const newNode = findNode<TreeNodeData>(newKey, treeData.value);
    if (newNode) {
      newNode.editable = true;
      currName.value = '';
      nextTick(() => {
        renameInput.value?.focus();
      });
    }
  };

  // 新增子节点
  const addChildren = (parentNode: TreeNodeData) => {
    const newKey = `${createGroupGroupPrefix}${Date.now()}`;
    const children = parentNode.children || [];
    children.push({
      title: '新分组',
      key: newKey
    });
    parentNode.children = children;
    treeData.value = [...treeData.value];
    nextTick(() => {
      // 展开
      tree.value.expandNode(parentNode.key);
      // 编辑子节点
      const newNode = findNode<TreeNodeData>(newKey, treeData.value);
      if (newNode) {
        newNode.editable = true;
        currName.value = '';
        nextTick(() => {
          renameInput.value?.focus();
        });
      }
    });
  };

  // 保存节点
  const saveNode = async (key: string | number) => {
    modCount.value++;
    if (modCount.value !== 1) {
      return;
    }
    // 寻找节点
    const node = findNode<TreeNodeData>(key, treeData.value);
    if (!node) {
      return;
    }
    if (currName.value) {
      node.loading = true;
      try {
        // 创建节点
        if (isString(key) && key.startsWith(createGroupGroupPrefix)) {
          const parent = findParentNode<TreeNodeData>(key, treeData.value);
          if (parent.root) {
            parent.key = rootId;
          }
          // 创建
          const { data } = await createHostGroup({
            parentId: parent.key as number,
            name: currName.value
          });
          node.key = data;
        } else {
          // 重命名节点
          await updateHostGroupName({
            id: key as unknown as number,
            name: currName.value
          });
        }
        node.title = currName.value;
        node.editable = false;
      } catch (e) {
        // 重复 重新聚焦
        setTimeout(() => {
          renameInput.value?.focus();
        }, 100);
      } finally {
        node.loading = false;
      }
    } else {
      // 未输入数据 并且为创建则移除节点
      if (isString(key) && key.startsWith(createGroupGroupPrefix)) {
        // 寻找父节点
        const parent = findParentNode(key, treeData.value);
        if (parent) {
          // 根节点
          if (parent.root) {
            parent.children = treeData.value;
          }
          // 移除子节点
          if (parent.children) {
            for (let i = 0; i < parent.children.length; i++) {
              if (parent.children[i].key === key) {
                parent.children.splice(i, 1);
              }
            }
          }
        }
      }
      node.editable = false;
    }
    modCount.value = 0;
  };

  // 移动分组
  const moveGroup = async (
    {
      dragNode, dropNode, dropPosition
    }: {
      dragNode: TreeNodeData,
      dropNode: TreeNodeData,
      dropPosition: number
    }) => {
    try {
      emits('loading', true);
      // 移动
      await moveHostGroup({
        id: dragNode.key as number,
        targetId: dropNode.key as number,
        position: dropPosition
      });
      // 移动分组
      moveNode(treeData.value, dragNode, dropNode, dropPosition);
    } catch (e) {
    } finally {
      emits('loading', false);
    }
  };

  // 加载数据
  const fetchTreeData = async (force = false) => {
    try {
      const groups = await cacheStore.loadHostGroups(force);
      emits('loading', true);
      treeData.value = groups;
    } catch (e) {
    } finally {
      emits('loading', false);
    }
    // 未选择则选择首个
    if (!tree.value?.getSelectedNodes()?.length && treeData.value.length) {
      await nextTick(() => {
        tree.value?.selectNode(treeData.value[0].key);
        emits('selectNode', treeData.value[0]);
      });
    }
  };

  defineExpose({ addRootNode, fetchTreeData });

  onMounted(() => {
    fetchTreeData();
  });

</script>

<style lang="less" scoped>
  :deep(.arco-scrollbar-container) {
    height: 100%;
    overflow-y: auto;
  }

  .tree-container {
    min-width: 100%;
    width: max-content;
    user-select: none;
    overflow: hidden;
  }

  .empty-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100%;
    padding-top: 25px;
    color: var(--color-text-3);
  }

  :deep(.arco-tree-node) {
    cursor: unset;

    .arco-tree-node-switcher {
      margin-left: 8px;
    }

    &:hover {
      background-color: var(--color-fill-1);
    }
  }

  :deep(.arco-tree-node-selected) {
    background-color: var(--color-fill-2);

    &:hover {
      background-color: var(--color-fill-1);
    }
  }

  :deep(.arco-tree-node-title) {
    padding: 0 80px 0 0;
    height: 32px;

    &:hover {
      background-color: var(--color-fill-1);
    }

    .arco-tree-node-title-text {
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      cursor: pointer;
    }
  }

  .node-title-wrapper {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    padding-left: 8px;
  }

  .tree-icon {
    font-size: 12px;
    color: rgb(var(--primary-6));
  }

</style>
