<template>
  <div class="permi-select-container">
    <div class="tree-header" v-if="$slots.header">
      <slot name="header"></slot>
    </div>
    
    <div class="tree-body">
      <tree-node
        v-for="node in data"
        :key="node.id"
        :node="node"
        :selected-keys="computedSelectedKeys"
        :indeterminate-keys="indeterminateKeys"
        :disabled-keys="disabledKeys"
        :expanded-keys="expandedKeys"
        @check="handleCheck"
        @expand="handleExpand"
      >
        <!-- 转发插槽 -->
        <template #default="{ node: childNode, data: childData }">
          <slot :node="childNode" :data="childData"></slot>
        </template>
      </tree-node>
    </div>
    
    <div class="tree-footer" v-if="$slots.footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue';
import TreeNode from './TreeNode.vue';
import type { TreeData, CheckedKeys } from './types.ts';

interface Props {
  data?: TreeData[];
  selectedKeys?: (string | number)[];
  disabled?: boolean;
}

interface Emits {
  (e: 'update:selectedKeys', keys: (string | number)[]): void;
  (e: 'check', keys: CheckedKeys): void;
}

const props = withDefaults(defineProps<Props>(), {
  data: () => [],
  selectedKeys: () => [],
  disabled: false
});

const emit = defineEmits<Emits>();

// 内部状态
const innerSelectedKeys = ref<(string | number)[]>([]);
const expandedKeys = ref<Set<string | number>>(new Set());
const indeterminateKeys = ref<Set<string | number>>(new Set());

/**
 * 视觉上的级联选中键值（核心显示效果）
 * 如果父节点被选中，其所有子孙节点在视觉上都显示为选中状态
 */
const computedSelectedKeys = computed(() => {
  const allSelectedKeys = new Set(innerSelectedKeys.value);

  const addDescendants = (nodes: TreeData[], parentSelected: boolean) => {
    nodes.forEach((node) => {
      // 如果父节点已被视觉选中，或者当前节点本身被选中的话
      const isSelected = parentSelected || innerSelectedKeys.value.includes(node.id);

      if (isSelected) {
        allSelectedKeys.add(node.id);
      }

      if (node.children && node.children.length > 0) {
        addDescendants(node.children, isSelected);
      }
    });
  };

  addDescendants(props.data, false);
  return Array.from(allSelectedKeys);
});

// 计算禁用的键值
const disabledKeys = computed(() => {
  const keys = new Set<number>();
  if (props.disabled) {
    const collectKeys = (nodes: TreeData[]) => {
      nodes.forEach((node) => {
        keys.add(node.id);
        if (node.children && node.children.length > 0) {
          collectKeys(node.children);
        }
      });
    };
    collectKeys(props.data);
  }
  return keys;
});

/** 更新半选状态 */
const updateIndeterminateState = () => {
  const newIndeterminateKeys = new Set<number>();

  const traverse = (nodes: TreeData[]) => {
    nodes.forEach((node) => {
      if (node.children && node.children.length > 0) {
        traverse(node.children);

        // 核心：如果子节点中有被选中的（不管是实际选中还是视觉选中）或者有半选的，则父节点为半选
        // 注意：由于逻辑是选中父节点就移除子节点，所以这里需要检查视觉选中集合
        const childVisibleKeys = node.children.map(c => c.id);
        const hasCheckedChild = childVisibleKeys.some(id => innerSelectedKeys.value.includes(id) || newIndeterminateKeys.has(id));
        
        // 特殊逻辑：由于我们选中父节点会移除子节点，
        // 只有当父节点未被选中，但子节点被选中时，父节点才显示半选
        if (hasCheckedChild && !innerSelectedKeys.value.includes(node.id)) {
          newIndeterminateKeys.add(node.id);
        }
      }
    });
  };

  traverse(props.data);
  indeterminateKeys.value = newIndeterminateKeys;
};

/** 获取所有后代节点 ID */
const getAllDescendantIds = (node: TreeData): (string | number)[] => {
  const ids: (string | number)[] = [];
  const traverse = (children?: TreeData[]) => {
    if (children) {
      children.forEach((child) => {
        ids.push(child.id);
        traverse(child.children);
      });
    }
  };
  traverse(node.children);
  return ids;
};

/** 查找节点路径（用于查找祖先） */
const findNodePath = (nodes: TreeData[], targetId: string | number): TreeData[] | null => {
  for (const node of nodes) {
    if (node.id === targetId) return [node];
    if (node.children) {
      const childPath = findNodePath(node.children, targetId);
      if (childPath) return [node, ...childPath];
    }
  }
  return null;
};

/** 处理节点选中与取消 */
const handleCheck = (node: TreeData, checked: boolean) => {
  if (props.disabled || disabledKeys.value.has(node.id)) return;

  let newSelectedKeys = [...innerSelectedKeys.value];
  const keyIndex = newSelectedKeys.indexOf(node.id);

  if (checked) {
    if (keyIndex === -1) {
      newSelectedKeys.push(node.id);
    }

    /** 核心逻辑：
     * 1. 选中当前节点。
     * 2. 移除所有后代，因为当前节点已经代表了整个分支。
     * 3. 移除所有祖先，因为祖先如果被选中，当前节点的选中就是冗余的，
     *    且如果是因为点击了子节点而取消祖先，表示更细粒度的控制。
     */
    const descendantIds = getAllDescendantIds(node);
    newSelectedKeys = newSelectedKeys.filter((id) => !descendantIds.includes(id));

    const path = findNodePath(props.data, node.id);
    if (path) {
      const ancestorIds = path.slice(0, -1).map((a) => a.id);
      newSelectedKeys = newSelectedKeys.filter((id) => !ancestorIds.includes(id));
    }
  } else {
    /** 核心逻辑：取消选中时
     * 如果当前节点在选中列表中，直接移除即可。
     */
    if (keyIndex !== -1) {
      newSelectedKeys.splice(keyIndex, 1);
    }
    
    /** 进阶：如果点击的是一个视觉选中的节点（其祖先在列表中）
     * 应该取消那个选中的祖先，并选中除该节点及其路径外的所有同级节点？
     * 但根据用户提供的 snippet，简单的逻辑是：点击哪个选中哪个。
     * 视觉选中效果主要用于反馈覆盖范围。
     */
  }

  innerSelectedKeys.value = newSelectedKeys;
  updateIndeterminateState();

  emit('update:selectedKeys', newSelectedKeys);
  emit('check', {
    checked: newSelectedKeys,
    indeterminate: Array.from(indeterminateKeys.value)
  });
};

/** 处理展开/折叠 */
const handleExpand = (nodeId: string | number) => {
  const newExpandedKeys = new Set(expandedKeys.value);
  if (newExpandedKeys.has(nodeId)) {
    newExpandedKeys.delete(nodeId);
  } else {
    newExpandedKeys.add(nodeId);
  }
  expandedKeys.value = newExpandedKeys;
};

// 初始化与同步
const initialize = () => {
  innerSelectedKeys.value = [...props.selectedKeys];
  updateIndeterminateState();
  
  // 默认展开全部
  const expandAll = (nodes: TreeData[]) => {
    nodes.forEach((node) => {
      expandedKeys.value.add(node.id);
      if (node.children) expandAll(node.children);
    });
  };
  expandAll(props.data);
};

onMounted(initialize);
watch(() => props.selectedKeys, (val) => {
  innerSelectedKeys.value = [...val];
  updateIndeterminateState();
}, { deep: true });
watch(() => props.data, updateIndeterminateState, { deep: true });

</script>

<style scoped lang="scss">
.permi-select-container {
  width: 100%;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  background-color: #fff;
  overflow: hidden;
}

.tree-header {
  padding: 6px 12px;
  border-bottom: 1px solid #f0f2f5;
  background-color: #fafbfc;
}

.tree-body {
  padding: 4px;
  max-height: 160px; /* 控制高度并显示滚动条 */
  overflow-y: auto;
  
  /* 自定义滚动条 */
  &::-webkit-scrollbar { width: 4px; }
  &::-webkit-scrollbar-thumb {
    background: #e4e7ed;
    border-radius: 4px;
  }
}

.tree-footer {
  padding: 6px 12px;
  border-top: 1px solid #f0f2f5;
  background-color: #fafbfc;
}
</style>
