<template>
  <div class="kole-tree">
    <div
      v-for="item in visibleList" :key="item.node.key"
      class="kole-tree-node"
      :class="{ 'is-selected': isSelected(item.node.key) }"
      :style="{ paddingLeft: item.depth * 16 + 4 + 'px' }"
      @click="onRowClick(item.node)"
    >
      <span
        v-if="hasChildren(item.node)"
        class="kole-tree-arrow"
        :class="{ 'is-open': isExpanded(item.node.key) }"
        @click.stop="toggleExpand(item.node.key)"
      >▸</span>
      <span v-else class="kole-tree-arrow-placeholder"></span>

      <input
        v-if="checkable"
        type="checkbox" class="kole-tree-check"
        :checked="isChecked(item.node.key)"
        :indeterminate.prop="isIndeterminate(item.node.key)"
        @click.stop="toggleCheck(item.node.key)"
      >
      <span class="kole-tree-label">{{ item.node.title }}</span>
    </div>
  </div>
</template>

<script setup>
import { computed, reactive } from 'vue';

const props = defineProps({
  data: { type: Array, required: true },          // [{ key, title, children, disabled }]
  checkable: { type: Boolean, default: false },
  defaultExpandedKeys: { type: Array, default: () => [] },
  selectedKeys: { type: Array, default: () => [] },   // v-model:selectedKeys
  checkedKeys: { type: Array, default: () => [] }     // v-model:checkedKeys
});
const emit = defineEmits(['update:selectedKeys', 'select', 'update:checkedKeys', 'check']);

const state = reactive({
  expanded: new Set(props.defaultExpandedKeys),
  selected: new Set(props.selectedKeys),
  checked: new Set(props.checkedKeys)
});

const hasChildren = (n) => !!(n.children && n.children.length);
function findNode(key, nodes = props.data) {
  for (const n of nodes) {
    if (n.key === key) return n;
    if (n.children) { const f = findNode(key, n.children); if (f) return f; }
  }
  return null;
}
function descendantKeys(node) {
  let out = [];
  (node.children || []).forEach(c => { out.push(c.key); out = out.concat(descendantKeys(c)); });
  return out;
}
function allChildrenChecked(node) {
  if (!hasChildren(node)) return state.checked.has(node.key);
  return node.children.every(c => allChildrenChecked(c));
}

const visibleList = computed(() => {
  const out = [];
  const walk = (nodes, depth) => {
    nodes.forEach(n => {
      out.push({ node: n, depth });
      if (hasChildren(n) && state.expanded.has(n.key)) walk(n.children, depth + 1);
    });
  };
  walk(props.data, 0);
  return out;
});

function isExpanded(k) { return state.expanded.has(k); }
function isSelected(k) { return state.selected.has(k); }
function isChecked(k) { return state.checked.has(k); }
function isIndeterminate(k) {
  const node = findNode(k);
  if (!node || !hasChildren(node)) return false;
  const desc = descendantKeys(node);
  return desc.some(d => state.checked.has(d)) && !allChildrenChecked(node);
}

function toggleExpand(key) {
  state.expanded.has(key) ? state.expanded.delete(key) : state.expanded.add(key);
  state.expanded = new Set(state.expanded);
}
function onRowClick(node) {
  state.selected = new Set([node.key]);
  emit('update:selectedKeys', [node.key]);
  emit('select', node.key);
}
function toggleCheck(key) {
  const node = findNode(key);
  const willCheck = !state.checked.has(key);
  const keys = [key, ...descendantKeys(node)];
  keys.forEach(k => willCheck ? state.checked.add(k) : state.checked.delete(k));
  const propagate = (nodes) => {
    nodes.forEach(n => {
      if (hasChildren(n)) {
        if (allChildrenChecked(n)) state.checked.add(n.key); else state.checked.delete(n.key);
        propagate(n.children);
      }
    });
  };
  propagate(props.data);
  state.checked = new Set(state.checked);
  emit('update:checkedKeys', [...state.checked]);
  emit('check', [...state.checked]);
}
</script>

<!-- 样式对齐 组件2.txt Tree 规范 -->
<style scoped>
.kole-tree { min-width: 240px; border: 1px solid #E8ECF1; border-radius: 8px; padding: 4px; background: #fff; }
.kole-tree-node {
  display: flex; align-items: center; gap: 4px; height: 32px; border-radius: 4px;
  cursor: pointer; user-select: none; font-size: 14px; color: #262626; padding-right: 8px;
}
.kole-tree-node:hover { background: #F5F7FA; }
.kole-tree-node.is-selected { background: #F0F5FF; color: #2F54EB; }
.kole-tree-arrow {
  display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px;
  font-size: 12px; color: #6E6E6E; transition: transform .15s; flex-shrink: 0;
}
.kole-tree-arrow.is-open { transform: rotate(90deg); }
.kole-tree-arrow-placeholder { width: 16px; flex-shrink: 0; }
.kole-tree-check { width: 14px; height: 14px; flex-shrink: 0; cursor: pointer; }
.kole-tree-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
</style>
