<template>
  <my-layout :radius="8">
    <layout-item>
      <div p-15 border-bottom>
        <slot name="header"></slot>
        <el-input v-model="name" flex-grow :placeholder="placeholder">
          <template #append>
            <el-button :icon="Search" @click="search" />
          </template>
        </el-input>
        <slot name="prepend"></slot>
      </div>
    </layout-item>
    <layout-main>
      <el-tree accordion ref="treeRef" :default-expanded-keys="defaultExpandedKeys" :draggable="canDrag" :style="style"
        :data="treeData" :allow-drag="allowDrag" :allow-drop="allowDrop" node-key="_id"
        :props="{ class: 'classify-list-tree' }" :expand-on-click-node="false" :filter-node-method="filterNode"
        @node-drag-start="handleDragStart" @node-drop="nodeDrop" @node-click="onNodeClick">
        <template #default="{ node, data }">
          <div class="w-0 flex-grow flex items-center h-full px-15px border-b border-gray-200">
            <div class="flex-shrink-0 mr-6px text-14px"
              :class="data.children && data.children.length ? '' : 'opacity-0'">
              <el-icon v-if="node.expanded">
                <CaretBottom />
              </el-icon>
              <el-icon v-else>
                <CaretRight />
              </el-icon>
            </div>
            <div class="flex-shrink-0 text-yellow-500 text-16px mr-6px">
              <el-icon>
                <Folder />
              </el-icon>
            </div>

            <div class="w-0 flex-grow truncate"
              :class="checkedCate && checkedCate._id === data._id ? 'text-blue-500' : ''">
              {{ node.label }}
            </div>
            <div class="flex-shrink-0 flex items-center" @click.stop="() => {}">
              <el-dropdown flex-shrink-0>
                <el-icon class="outline-none">
                  <MoreFilled />
                </el-icon>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item v-if="data.level < level" @click.stop="addChild(data)">
                      <div class="px-12px text-14px py-4px">{{ getLabel(data, 'add') }}</div>
                    </el-dropdown-item>
                    <el-dropdown-item @click.stop="rename(data)">
                      <div class="px-12px text-14px py-4px" color-primary>{{ getLabel(data, 'edit') }}</div>
                      <!-- {{custom ? '编辑':'重命名'}} -->
                    </el-dropdown-item>
                    <el-dropdown-item @click.stop="remove(data)">
                      <div class="px-12px text-14px py-4px" color-danger>{{ getLabel(data, 'remove') }}</div>
                    </el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>

            <span v-if="data.level === 1" class="ml-8px flex-shrink-0" title="拖拽可进行排序">
              <svg-icon style="color: #999" size="16" name="drag"></svg-icon>
            </span>
          </div>
        </template>
      </el-tree>
    </layout-main>
    <layout-item>
      <div p-15 class="border-t border-gray-200">
        <el-button class="w-full" type="primary" @click="add()">新建一级{{ label }}</el-button>
      </div>
    </layout-item>
  </my-layout>
  <el-dialog v-model="visible" :title="current._id ? `重命名${label}` : `新增${label}`" :width="width">
    <div p-15>
      <div v-if="!current._id" color-normal class="mb-10px">
        <span v-if="current.parent" color-normal font-14>在【{{ current.parent.label }}】下方新增{{ label }}</span>
        <span v-else color-normal font-14>添加一级{{ label }}</span>
      </div>
      <el-input v-model="current.label" :maxlength="10" :placeholder="current.label || `请输入${label}名称`" />
    </div>
    <template #footer>
      <div text-center>
        <el-button @click="close()">取消</el-button>
        <el-button type="primary" @click="save()">保存</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
import { computed, nextTick, ref } from "vue";
import { watchDebounced } from "@vueuse/core";
import useModal from "@/hooks/useModal";
import { getCateTreeData } from "@/utils/index";

import { Search } from "@element-plus/icons-vue";
import MyLayout, { LayoutMain, LayoutItem } from "@/components/layout";
import SvgIcon from "@/components/svg-icon";
import { ElMessage, ElMessageBox } from "element-plus";

const default_label = {
  add: "新增",
  edit: '重命名',
  remove: "删除"
}

const emits = defineEmits(["change", "search", "onHandle"]);
const props = defineProps({
  addMethod: {
    type: Function,
    default: () => { },
  },
  checkedCate: {
    type: Object,
    default: () => ({}),
  },
  custom: { type: Boolean, default: false },
  label: { type: String, default: "分类" },
  labelOption: {
    default: () => ({}),
    type: [Object, Function]
  },
  level: { type: Number, default: 3 },
  data: {
    type: Array,
    default: () => [],
  },
  placeholder: { type: String, default: "请输入分类名称" },
  removeMethod: {
    type: Function,
    default: () => { },
  },
  sortMethod: {
    type: Function,
    default: () => { },
  },
  updateMethod: {
    type: Function,
    default: () => { },
  },
});

const style = {
  "--el-tree-node-content-height": "40px",
};

const name = ref("");
const treeRef = ref();
const treeExpandMap = ref({});
const treeData = ref();
const canDrag = computed(() => name.value.trim() === "");
const defaultExpandedKeys = ref([]);

function search() {
  emits("search", name.value.trim());
}

function filterNode(value, data) {
  return value ? data.label.includes(value) : true;
}

async function onNodeClick(data, node, treeNode) {
  if (data._id !== props.checkedCate._id) {
    emits("change", { label: data.label, _id: data._id, childrenIds: data.childrenIds });
  }
  if (data.children && data.children.length) {
    // node.expanded = !node.expanded;
    node.expanded = !node.expanded;
    treeExpandMap.value[data._id] = node.expanded;
    defaultExpandedKeys.value = Object.keys(treeExpandMap.value).filter((key) => treeExpandMap.value[key]);
  }
  // const nodesMap = treeRef.value.store.nodesMap;
  // if (!nodesMap[data._id]) return;
  // if (data.children && data.children.length) {
  //   nodesMap[data._id].expanded = !nodesMap[data._id].expanded;
  //   treeExpandMap.value = Object.keys(nodesMap).reduce((acc, key) => {
  //     const node = nodesMap[key];
  //     if (node.expanded) {
  //       acc[key] = node.expanded;
  //     }
  //     return acc;
  //   }, {});
  //   defaultExpandedKeys.value = Object.keys(treeExpandMap.value);
  // } else {
  //   nodesMap[data._id].checked = !nodesMap[data._id].checked;
  // }
}

function allowDrag(node) {
  return node.level === 1;
}

function allowDrop(draggingNode, dropNode, type) {
  return ["next", "prev"].includes(type) && dropNode.level === 1;
}

function handleDragStart(node, event) { }

async function nodeDrop() {
  const changedOrder = treeData.value.map((item, idx) => ({ _id: item._id, label: item.label, sort: idx }));
  const { success, message } = await props.sortMethod(changedOrder);

  if (!success) {
    ElMessage.error(message);
  }
}

const { close, show, visible, width } = useModal(560);
const current = ref({});
function add() {
  if (props.custom) {
    emits("onHandle", { _type: "add", level: 1 });
  } else {
    current.value = { label: "", _type: "add" };
    show();
  }
}

function addChild({ level, _id, label }) {
  if (props.custom) {
    emits("onHandle", { parentId: _id, parentName: label, _type: "addChild" });
  } else {
    current.value = {
      level: level + 1,
      _type: "addChild",
      parent: { _id, label },
    };
    show();
  }
}

function getLabel(data, type) {
  const option = Object.prototype.toString.call(props.labelOption) === '[object Object]' ? props.labelOption : {};
  const labels = { ...default_label, ...option };
  if (typeof props.labelOption === 'function') {
    const label = props.labelOption(data, type);
    return label || labels[type];
  }
  return labels[type]
}

function rename(data) {
  const { _id, label } = data
  if (props.custom) {
    emits("onHandle", { ...data, _type: 'edit' });
  } else {
    current.value = { _id, label, _type: "rename" };
    show();
  }
}

async function remove({ _id, label }) {
  await ElMessageBox.confirm(`确认删除分类下【${label}】所有的内容吗？`, "提示", { type: "warning" });
  const { success, message } = await props.removeMethod(_id);
  if (success) {
    ElMessage.success(message);
  } else {
    ElMessage.error(message);
  }
  close();
}

async function save() {
  if (typeof current.value.label !== "string" || current.value.label.trim() === "") {
    ElMessage.info("分类名称不能为空");
    return;
  }
  let fn;
  let params = {};
  if (current.value._type === "add") {
    fn = props.addMethod;
    params = { label: current.value.label };
  } else if (current.value._type === "addChild") {
    fn = props.addMethod;
    params = { label: current.value.label, parentId: current.value.parent._id };
  } else if (current.value._type === "rename") {
    fn = props.updateMethod;
    params = { id: current.value._id, label: current.value.label };
  }
  const { success, message } = await fn(params);
  if (success) {
    ElMessage.success(message);
  } else {
    ElMessage.error(message);
  }
  close();
}

function findCurrentNode(_id, tree) {
  const item = props.data.find((i) => _id && i._id === _id);
  if (item) {
    const ids = [_id];
    let parentId = item.parentId;
    while (parentId) {
      const parent = props.data.find((i) => parentId && i._id === parentId);
      if (parent) {
        ids.unshift(parent._id);
        parentId = parent.parentId;
      } else {
        parentId = null;
      }
    }
    let node = null;
    let list = tree;
    for (let i = 0; i < ids.length; i++) {
      const id = ids[i];
      node = list.find((i) => i._id === id);
      if (node) {
        list = node.children;
      } else {
        break;
      }
    }
    return node;
  }
  return tree[0];
}

watchDebounced(name, (val, old) => {
  if (val.trim() !== old.trim()) {
    treeRef.value && treeRef.value.filter(val.trim());
  }
});

watchDebounced(
  () => props.data,
  async (n) => {
    treeData.value = getCateTreeData(n);
    if (treeData.value.length === 0) {
      emits("change", {});
      return;
    }
    const currentNode = findCurrentNode(props.checkedCate._id, treeData.value);
    emits("change", { label: currentNode.label, _id: currentNode._id, childrenIds: currentNode.childrenIds });
    await nextTick();
    if (name.value && name.value.trim()) {
      treeRef.value && treeRef.value.filter(name.value.trim());
    }
  },
  { immediate: true }
);

</script>
<style lang="scss" scoped>
:deep(.el-input-group__append) {
  background-color: #fff;
}

:deep(.classify-list-tree > .el-tree-node__content > .el-tree-node__expand-icon) {
  display: none;
}
</style>
