<script setup lang="ts">
import type { CategoryNode } from '../types/category'

const props = defineProps<{
  node: CategoryNode
  depth: number
  expandDepth: number
  expandedMap: Record<number, true>
  collapsedMap: Record<number, true>
}>()

const emit = defineEmits<{
  toggle: [id: number]
}>()

const hasChildren = () => props.node.children.length > 0

function isExpanded(): boolean {
  if (!hasChildren()) return false
  if (props.collapsedMap[props.node.id]) return false
  if (props.expandedMap[props.node.id]) return true
  return props.depth < props.expandDepth
}
</script>

<template>
  <div class="tree-node">
    <div class="node-card" :class="{ leaf: !hasChildren(), open: isExpanded() }">
      <button
        v-if="hasChildren()"
        class="toggle"
        type="button"
        :aria-expanded="isExpanded()"
        :title="isExpanded() ? '收起' : '展开'"
        @click="emit('toggle', node.id)"
      >
        {{ isExpanded() ? '−' : '+' }}
      </button>
      <span v-else class="toggle-spacer" />
      <div class="node-name">{{ node.name || '(未命名)' }}</div>
    </div>

    <ul v-if="hasChildren() && isExpanded()" class="children">
      <li
        v-for="child in node.children"
        :key="child.id"
        class="child"
      >
        <TreeNode
          :node="child"
          :depth="depth + 1"
          :expand-depth="expandDepth"
          :expanded-map="expandedMap"
          :collapsed-map="collapsedMap"
          @toggle="emit('toggle', $event)"
        />
      </li>
    </ul>
  </div>
</template>

<style scoped>
/* 节点卡片垂直中线，用于对齐连线 */
.tree-node {
  --line-y: 20px;
  --line-color: #cbd5e1;
  --gap: var(--col-gap, 36px);

  display: flex;
  flex-direction: row;
  align-items: flex-start;
}

.node-card {
  display: flex;
  align-items: center;
  gap: 8px;
  width: var(--col-width, 180px);
  min-height: 40px;
  padding: 8px 10px;
  border: 1px solid #d8dee6;
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
  flex-shrink: 0;
  box-sizing: border-box;
}

.node-card.open {
  border-color: #9eb6d4;
  background: #f7faff;
}

.node-card.leaf {
  border-style: dashed;
  background: #fafbfc;
}

.toggle {
  width: 22px;
  height: 22px;
  border: 1px solid #c5ced9;
  border-radius: 4px;
  background: #fff;
  color: #334155;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}

.toggle:hover {
  border-color: #64748b;
  background: #f1f5f9;
}

.toggle-spacer {
  width: 22px;
  flex-shrink: 0;
}

.node-name {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  font-weight: 600;
  color: #0f172a;
  line-height: 1.35;
  word-break: break-word;
}

.children {
  --half-gap: calc(var(--gap) / 2);
  --sibling-gap: 10px;

  display: flex;
  flex-direction: column;
  /* 不用 flex gap：空隙不在子项盒模型内，竖线会断开 */
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--gap);
  position: relative;
}

/* 父节点 → 竖脊：左半段水平线 */
.children::before {
  content: '';
  position: absolute;
  left: 0;
  top: var(--line-y);
  width: var(--half-gap);
  border-top: 2px solid var(--line-color);
}

.child {
  position: relative;
}

/* 间距放进 padding，让竖脊 ::after 能连续穿过 */
.child:not(:last-child) {
  padding-bottom: var(--sibling-gap);
}

/* 竖脊 → 子节点：右半段水平线 */
.child::before {
  content: '';
  position: absolute;
  top: var(--line-y);
  left: 0;
  width: var(--half-gap);
  margin-left: calc(-1 * var(--half-gap));
  border-top: 2px solid var(--line-color);
}

/* 兄弟之间的竖脊（位于 gap 中点） */
.child::after {
  content: '';
  position: absolute;
  left: 0;
  margin-left: calc(-1 * var(--half-gap));
  border-left: 2px solid var(--line-color);
}

/* 中间兄弟：通栏竖线（含底部 padding） */
.child:not(:first-child):not(:last-child)::after {
  top: 0;
  bottom: 0;
}

/* 第一个兄弟：从中线画到自身底部（含 padding） */
.child:first-child:not(:last-child)::after {
  top: var(--line-y);
  bottom: 0;
}

/* 最后一个兄弟：从顶部画到中线 */
.child:last-child:not(:first-child)::after {
  top: 0;
  height: var(--line-y);
}

/* 唯一子节点：无需竖脊（水平线已贯通） */
.child:only-child::after {
  display: none;
}
</style>
