<script setup>
import { ref, computed, watch } from 'vue'
import { NModal, NInput, NIcon, NTag, NEmpty, NSpin } from 'naive-ui'
import { SearchOutline } from '@vicons/ionicons5'
import { getAllTagsForSelector, setEntityTags } from '../../api/tag'

const props = defineProps({
  show: { type: Boolean, default: false },
  entityType: { type: String, default: 'skill' },
  entityId: { type: String, default: '' },
  selectedTagIds: { type: Array, default: () => [] }
})

const emit = defineEmits(['update:show', 'saved'])

const loading = ref(false)
const saving = ref(false)
const keyword = ref('')
const rawData = ref([])
const selectedIds = ref(new Set([]))

// 构建树：跳过虚拟根节点
function buildTree(tags) {
  const map = {}
  const roots = []
  for (const t of tags) {
    map[t.id] = { ...t, children: [] }
  }
  for (const t of tags) {
    if (t.parentId === null) {
      for (const child of tags) {
        if (child.parentId === t.id && map[child.id]) {
          roots.push(map[child.id])
        }
      }
    } else if (map[t.parentId]) {
      map[t.parentId].children.push(map[t.id])
    }
  }
  return roots
}

// 扁平化所有标签
const allFlatTags = computed(() => {
  const result = []
  for (const g of rawData.value) {
    for (const t of g.tags || []) {
      if (t.parentId === null) continue
      result.push({ id: t.id, name: t.name, groupId: g.id, groupName: g.name, parentId: t.parentId })
    }
  }
  return result
})

// 构建所有标签组的树（含组名根节点）
const groupTrees = computed(() => {
  return rawData.value.map(g => ({
    id: g.id,
    name: g.name,
    tree: buildTree(g.tags || [])
  }))
})

const searchResults = computed(() => {
  if (!keyword.value.trim()) return []
  const kw = keyword.value.trim().toLowerCase()
  return allFlatTags.value.filter(t =>
    t.name.toLowerCase().includes(kw) || t.groupName.toLowerCase().includes(kw)
  )
})

function getTagPath(tagId) {
  const parts = []
  let current = allFlatTags.value.find(t => t.id === tagId)
  while (current) {
    parts.unshift(current.name)
    current = allFlatTags.value.find(t => t.id === current.parentId)
  }
  return parts.join(' / ')
}

function getTagName(tagId) {
  const t = allFlatTags.value.find(t => t.id === tagId)
  return t ? t.name : `#${tagId}`
}

watch(() => props.show, async (val) => {
  if (val) {
    selectedIds.value = new Set(props.selectedTagIds)
    keyword.value = ''
    await loadData()
  }
})

async function loadData() {
  loading.value = true
  try {
    const res = await getAllTagsForSelector()
    rawData.value = res.data || []
  } catch {} finally {
    loading.value = false
  }
}

function toggleTag(tagId) {
  const s = new Set(selectedIds.value)
  if (s.has(tagId)) s.delete(tagId)
  else s.add(tagId)
  selectedIds.value = s
}

async function handleSave() {
  if (!props.entityId) return
  saving.value = true
  try {
    await setEntityTags(props.entityType, props.entityId, [...selectedIds.value])
    emit('saved', [...selectedIds.value])
    emit('update:show', false)
  } catch {} finally {
    saving.value = false
  }
}

// ========== 思维导图布局 ==========
const FONT_SIZE = 11
const NODE_H = 20          // 比文字略高
const NODE_PAD_X = 8
const GROUP_GAP_Y = 16
const LEVEL_GAP_X = 24
const NODE_GAP_Y = 4
const BORDER_RADIUS = NODE_H / 2  // 胶囊形

function layoutTree(groups) {
  const positions = []

  function measureNode(name) {
    let w = NODE_PAD_X * 2
    for (const ch of name) {
      w += ch.charCodeAt(0) > 127 ? 12 : 7
    }
    return Math.max(w, 32)
  }

  function subtreeHeight(nodes) {
    if (!nodes || nodes.length === 0) return 0
    let total = 0
    for (const n of nodes) {
      const childH = subtreeHeight(n.children)
      total += Math.max(NODE_H, childH) + NODE_GAP_Y
    }
    return Math.max(0, total - NODE_GAP_Y)
  }

  // 递归布局标签节点
  function layoutNodes(nodes, x, y, depth, groupId) {
    const totalH = subtreeHeight(nodes)
    let cy = y - totalH / 2

    for (const n of nodes) {
      const w = measureNode(n.name)
      const childH = subtreeHeight(n.children)
      const nodeCenterY = cy + Math.max(NODE_H, childH) / 2

      positions.push({
        id: n.id, name: n.name,
        x, y: nodeCenterY - NODE_H / 2,
        w, h: NODE_H,
        depth, groupId,
        isGroup: false
      })

      if (n.children && n.children.length > 0) {
        layoutNodes(n.children, x + w + LEVEL_GAP_X, nodeCenterY, depth + 1, groupId)
      }
      cy += Math.max(NODE_H, childH) + NODE_GAP_Y
    }
  }

  // 按组布局：组名作为根节点
  let offsetY = 0
  for (const g of groups) {
    const childH = subtreeHeight(g.tree)
    const groupCenterY = offsetY + Math.max(NODE_H, childH) / 2

    // 组名根节点（depth=0）
    const gw = measureNode(g.name)
    positions.push({
      id: 'group-' + g.id, name: g.name,
      x: 0, y: groupCenterY - NODE_H / 2,
      w: gw, h: NODE_H,
      depth: 0, groupId: g.id,
      isGroup: true
    })

    // 标签节点从组名右侧开始（depth=1）
    if (g.tree.length > 0) {
      layoutNodes(g.tree, gw + LEVEL_GAP_X, groupCenterY, 1, g.id)
    }

    offsetY += Math.max(NODE_H, childH) + GROUP_GAP_Y
  }

  return positions
}

// 思维导图数据
const mindmapData = computed(() => {
  if (!groupTrees.value.length) return { nodes: [], lines: [], width: 0, height: 0 }

  const positions = layoutTree(groupTrees.value)
  if (positions.length === 0) return { nodes: [], lines: [], width: 0, height: 0 }

  let maxX = 0, maxY = 0
  for (const p of positions) {
    if (p.x + p.w > maxX) maxX = p.x + p.w
    if (p.y + p.h > maxY) maxY = p.y + p.h
  }

  // 生成贝塞尔曲线连线
  const lines = []
  const posMap = {}
  for (const p of positions) {
    if (!p.isGroup) posMap[p.id] = p
  }

  // 建立组名根节点位置映射
  const groupPosMap = {}
  for (const p of positions) {
    if (p.isGroup) groupPosMap[p.groupId] = p
  }

  // 从原始 tags 关系生成父子连线
  for (const g of rawData.value) {
    const groupRoot = groupPosMap[g.id]
    for (const t of g.tags || []) {
      if (t.parentId === null) continue
      const childPos = posMap[t.id]
      if (!childPos) continue

      // 判断父节点：如果是虚拟根节点的直接子节点，连线从组名节点出发
      const parentPos = posMap[t.parentId] || null
      if (parentPos) {
        // 普通父子连线
        const x1 = parentPos.x + parentPos.w
        const y1 = parentPos.y + parentPos.h / 2
        const x2 = childPos.x
        const y2 = childPos.y + childPos.h / 2
        const cx = (x1 + x2) / 2
        lines.push(`M ${x1} ${y1} C ${cx} ${y1}, ${cx} ${y2}, ${x2} ${y2}`)
      } else if (groupRoot) {
        // 一级标签：从组名节点连线
        const x1 = groupRoot.x + groupRoot.w
        const y1 = groupRoot.y + groupRoot.h / 2
        const x2 = childPos.x
        const y2 = childPos.y + childPos.h / 2
        const cx = (x1 + x2) / 2
        lines.push(`M ${x1} ${y1} C ${cx} ${y1}, ${cx} ${y2}, ${x2} ${y2}`)
      }
    }
  }

  return {
    nodes: positions,
    lines,
    width: maxX + 12,
    height: maxY + 12
  }
})
</script>

<template>
  <n-modal
    :show="show"
    preset="card"
    title="选择标签"
    style="max-width: 720px; width: 90vw"
    :bordered="false"
    :mask-closable="false"
    @update:show="$emit('update:show', $event)"
  >
    <!-- 搜索框 -->
    <div class="selector-search">
      <n-input v-model:value="keyword" placeholder="搜索标签..." clearable size="small">
        <template #prefix>
          <n-icon size="14"><SearchOutline /></n-icon>
        </template>
      </n-input>
    </div>

    <!-- 已选标签 -->
    <div v-if="selectedIds.size" class="selected-tags">
      <span class="selected-label">已选 {{ selectedIds.size }} 个</span>
      <div class="selected-list">
        <n-tag
          v-for="tagId in selectedIds"
          :key="tagId"
          size="small"
          closable
          @close="toggleTag(tagId)"
          :bordered="false"
          color="#16a34a"
          text-color="#fff"
        >
          {{ getTagName(tagId) }}
        </n-tag>
      </div>
    </div>

    <!-- 搜索模式 -->
    <div v-if="keyword.trim()" class="search-results">
      <div v-if="searchResults.length === 0" class="no-result">
        <n-empty description="无匹配标签" size="small" />
      </div>
      <div
        v-for="tag in searchResults"
        :key="tag.id"
        class="search-item"
        :class="{ selected: selectedIds.has(tag.id) }"
        @click="toggleTag(tag.id)"
      >
        <span class="si-name">{{ tag.name }}</span>
        <span class="si-path">{{ tag.groupName }} · {{ getTagPath(tag.id) }}</span>
      </div>
    </div>

    <!-- 思维导图树 -->
    <div v-else class="mindmap-container">
      <div v-if="loading" style="text-align: center; padding: 30px;">
        <n-spin size="small" />
      </div>
      <div v-else-if="rawData.length === 0">
        <n-empty description="暂无标签，请先在标签管理中创建" size="small" />
      </div>
      <div v-else-if="mindmapData.nodes.length === 0" style="padding: 20px; text-align: center; color: var(--text-tertiary); font-size: 13px;">
        标签组为空，请先添加标签
      </div>
      <div v-else class="mindmap-scroll">
        <svg
          class="mindmap-svg"
          :width="mindmapData.width"
          :height="mindmapData.height"
          :viewBox="`0 0 ${mindmapData.width} ${mindmapData.height}`"
        >
          <path
            v-for="(d, i) in mindmapData.lines"
            :key="'l' + i"
            :d="d"
            fill="none"
            stroke="rgba(255,255,255,0.25)"
            stroke-width="1"
          />
        </svg>
        <div class="mindmap-nodes" :style="{ width: mindmapData.width + 'px', height: mindmapData.height + 'px' }">
          <div
            v-for="node in mindmapData.nodes"
            :key="node.id"
            class="mindmap-node"
            :class="{
              selected: !node.isGroup && selectedIds.has(node.id),
              'is-group': node.isGroup
            }"
            :style="{
              left: node.x + 'px',
              top: node.y + 'px',
              width: node.w + 'px',
              height: node.h + 'px',
              borderRadius: BORDER_RADIUS + 'px'
            }"
            @click="!node.isGroup && toggleTag(node.id)"
          >
            <span class="node-text">{{ node.name }}</span>
          </div>
        </div>
      </div>
    </div>

    <template #action>
      <button class="btn-cancel" @click="$emit('update:show', false)">取消</button>
      <button class="btn-save" :disabled="saving" @click="handleSave">
        {{ saving ? '保存中...' : '确认' }}
      </button>
    </template>
  </n-modal>
</template>

<style scoped>
.selector-search { margin-bottom: 12px; }
.selected-tags { margin-bottom: 12px; }
.selected-label { font-size: 12px; color: var(--text-tertiary); margin-bottom: 6px; display: block; }
.selected-list { display: flex; flex-wrap: wrap; gap: 6px; }

/* 搜索结果 */
.search-results { max-height: 320px; overflow-y: auto; }
.search-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; border-radius: 5px; cursor: pointer; transition: background 0.15s; }
.search-item:hover { background: rgba(37, 99, 235, 0.06); }
.search-item.selected { background: rgba(22, 163, 74, 0.1); }
.si-name { font-size: 13px; color: var(--text-primary); font-weight: 500; }
.si-path { font-size: 11px; color: var(--text-tertiary); max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.no-result { padding: 24px 0; }

/* ========== 思维导图 ========== */
.mindmap-container { min-height: 200px; }

.mindmap-scroll {
  position: relative;
  overflow: auto;
  max-height: 420px;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.2);
}

.mindmap-svg { position: absolute; top: 0; left: 0; pointer-events: none; }
.mindmap-nodes { position: relative; }

.mindmap-node {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.5);
  background: transparent;
  cursor: pointer;
  transition: all 0.15s ease;
  user-select: none;
}

.mindmap-node:hover:not(.is-group) {
  border-color: rgba(255, 255, 255, 0.8);
}

.mindmap-node.selected {
  border-color: #22c55e;
}

.mindmap-node.selected .node-text {
  color: #22c55e;
}

.mindmap-node.is-group {
  cursor: default;
  font-weight: 600;
}

.mindmap-node.is-group .node-text {
  color: rgba(255, 255, 255, 0.85);
}

.node-text {
  font-size: 11px;
  color: rgba(255, 255, 255, 0.65);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 2px;
  line-height: 1;
}

/* 按钮 */
.btn-cancel { padding: 6px 16px; border-radius: 6px; border: 1px solid var(--border-color); background: transparent; color: var(--text-secondary); cursor: pointer; font-size: 13px; }
.btn-save { padding: 6px 16px; border-radius: 6px; border: none; background: var(--color-accent); color: white; cursor: pointer; font-size: 13px; margin-left: 8px; }
.btn-save:disabled { opacity: 0.6; cursor: not-allowed; }
</style>
