<script setup lang="ts">
import { computed } from 'vue'
import type { CategoryNode, WeightDimKey } from '../types/category'
import {
  formatAvgScore,
  heatColor,
  heatTextColor,
  weightHeatT,
} from '../types/category'
import type { DemandBelongItem, DemandsByCategory } from '../types/demand'

const props = defineProps<{
  node: CategoryNode
  depth: number
  expandDepth: number
  expandedMap: Record<number, true>
  collapsedMap: Record<number, true>
  demandsByCategory: DemandsByCategory
  activeDim: WeightDimKey | null
  weightScale: number[]
}>()

const emit = defineEmits<{
  toggle: [id: number]
  inspect: [payload: { categoryId: number; categoryName: string; items: DemandBelongItem[] }]
}>()

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
}

function demandsForNode(): DemandBelongItem[] {
  return props.demandsByCategory[props.node.id] ?? []
}

const weight = computed(() =>
  props.activeDim ? (props.node.weights?.[props.activeDim] ?? 0) : null,
)
const count = computed(() =>
  props.activeDim ? (props.node.counts?.[props.activeDim] ?? 0) : 0,
)

const heatT = computed(() => {
  if (!props.activeDim || count.value <= 0 || weight.value == null) return null
  return weightHeatT(weight.value, props.weightScale)
})

const cardStyle = computed(() => {
  const t = heatT.value
  const bg = heatColor(t)
  const color = heatTextColor(t)
  return {
    background: bg,
    color,
    borderColor: t == null ? '#d8dee6' : 'rgba(15, 23, 42, 0.12)',
  }
})

function onInspect(e: MouseEvent) {
  e.stopPropagation()
  const items = demandsForNode()
  emit('inspect', {
    categoryId: props.node.id,
    categoryName: props.node.name || '(未命名)',
    items,
  })
}
</script>

<template>
  <div class="tree-node">
    <div
      class="node-card"
      :class="{ leaf: !hasChildren(), open: isExpanded(), muted: activeDim != null && heatT == null }"
      :style="cardStyle"
      :title="
        !activeDim
          ? (node.name || '(未命名)')
          : count > 0
            ? `avg=${weight} count=${count}`
            : '无数据'
      "
    >
      <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-main">
        <div class="node-text">
          <span class="node-name">{{ node.name || '(未命名)' }}</span>
          <span v-if="activeDim" class="node-weight">
            {{ formatAvgScore(weight, count) }}
          </span>
        </div>
        <button
          v-if="demandsForNode().length"
          class="inspect"
          type="button"
          title="查看需求词"
          @click="onInspect"
        >
          🔍
        </button>
      </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"
          :demands-by-category="demandsByCategory"
          :active-dim="activeDim"
          :weight-scale="weightScale"
          @toggle="emit('toggle', $event)"
          @inspect="emit('inspect', $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;
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04);
  flex-shrink: 0;
  box-sizing: border-box;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.node-card.muted {
  opacity: 0.72;
}

.toggle {
  width: 22px;
  height: 22px;
  border: 1px solid rgba(15, 23, 42, 0.18);
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.65);
  color: #334155;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}

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

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

.node-main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-start;
  gap: 4px;
}

.node-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.node-name {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.3;
  word-break: break-word;
}

.node-weight {
  font-size: 11px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
  letter-spacing: 0.01em;
}

.inspect {
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  margin-top: -1px;
  border: none;
  border-radius: 4px;
  background: transparent;
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}

.inspect:hover {
  background: rgba(15, 23, 42, 0.08);
}

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

  display: flex;
  flex-direction: column;
  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;
}

.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);
}

.child::after {
  content: '';
  position: absolute;
  left: 0;
  margin-left: calc(-1 * var(--half-gap));
  border-left: 2px solid var(--line-color);
}

.child:not(:first-child):not(:last-child)::after {
  top: 0;
  bottom: 0;
}

.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>
