<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { NButton, NInput, NIcon, NSelect, NCheckbox, NTooltip, NSpin, NEmpty, NModal, useMessage, useDialog } from 'naive-ui'
import { ArrowBackOutline, SaveOutline, CodeSlashOutline, GlobeOutline, TimeOutline } from '@vicons/ionicons5'
import { IO_TYPES } from '../../utils/ioInference'
import { getLanguageFromPath } from '../../utils/language'
import { translateText } from '../../api/translation'
import { normalizeKebabCase, isValidKebabCase } from '../../utils/skillName'
import {
  getSkillDetail, saveSkillMd, listSkillFiles,
  readSkillFile, saveSkillFile, createSkillFile, deleteSkillFile,
  listVersions, getVersionContent, rollbackVersion, updateVersionMessage
} from '../../api/skill'
import CodeEditor from '../../components/skill/CodeEditor.vue'
import FileTree from '../../components/skill/FileTree.vue'

const route = useRoute()
const router = useRouter()
const message = useMessage()
const dialog = useDialog()

const folderName = route.params.folderName
const loading = ref(true)
const saving = ref(false)
const advancedMode = ref(false)

// ==================== 基础编辑模式数据 ====================
const form = ref({
  name: '',
  displayName: '',
  description: '',
  canGenerateSkill: false,
  inputs: [],
  outputs: []
})
const body = ref('')
const originalBody = ref('')
const originalForm = ref(null)

// ==================== 高级编辑模式数据 ====================
const fileTree = ref([])
const currentFilePath = ref('')
const currentFileContent = ref('')
const currentFileDirty = ref(false)

// ==================== 版本历史 ====================
const showVersionPanel = ref(false)
const versions = ref([])
const versionsLoading = ref(false)
const selectedVersion = ref(null)         // 当前查看的版本
const versionContent = ref('')            // 选中版本的内容
const diffVersion = ref(null)             // diff 对比的旧版本
const diffContent = ref('')              // diff 对比的旧版本内容
const editingMessage = ref(false)         // 是否正在编辑备注
const editingMessageText = ref('')        // 编辑中的备注文本

// ==================== 翻译 ====================
const isTranslating = ref(false)

// ==================== 名称实时校验 ====================
const nameState = computed(() => {
  const raw = form.value.name || ''
  if (!raw.trim()) return { status: 'empty', normalized: '', changed: false }
  const normalized = normalizeKebabCase(raw)
  const valid = isValidKebabCase(normalized)
  if (!valid) return { status: 'invalid', normalized, changed: false }
  // 优先级 1：规范化后与当前 folderName 不同 → 触发目录重命名
  if (normalized !== folderName) return { status: 'will-rename', normalized, changed: true }
  // 优先级 2：raw 不规范但规范化后等于 folderName → 仅规范化输入，目录不变
  if (normalized !== raw) return { status: 'will-normalize', normalized, changed: true }
  return { status: 'valid', normalized, changed: false }
})

function isEnglish(text) {
  if (!text || !text.trim()) return false
  for (let i = 0; i < text.length; i++) {
    const c = text.charCodeAt(i)
    if (c >= 0x4e00 && c <= 0x9fff) return false
  }
  return true
}

/** 检测文本是否含英文字母（用于正文等长文本；与 isEnglish「纯英文」语义区分开） */
function containsEnglish(text) {
  if (!text) return false
  for (let i = 0; i < text.length; i++) {
    const c = text.charCodeAt(i)
    // A-Z / a-z
    if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)) return true
  }
  return false
}

const hasEnglishContent = computed(() => {
  if (advancedMode.value) {
    return isTranslatableFile(currentFilePath.value) && containsEnglish(currentFileContent.value)
  }
  return isEnglish(form.value.displayName) || isEnglish(form.value.description) || containsEnglish(body.value)
})

/** 高级模式下仅允许 markdown / 纯文本文件翻译，避免损坏代码/配置文件结构 */
function isTranslatableFile(path) {
  if (!path) return false
  const ext = path.split('.').pop().toLowerCase()
  return ['md', 'markdown', 'txt'].includes(ext)
}

async function handleTranslate() {
  if (isTranslating.value) return
  isTranslating.value = true
  try {
    if (advancedMode.value) {
      // 高级编辑模式：对当前打开的文件做全文翻译（仅限 md/txt）
      const content = currentFileContent.value
      if (!isTranslatableFile(currentFilePath.value)) {
        message.warning('仅支持 Markdown / 文本文件的翻译')
        return
      }
      if (!containsEnglish(content)) {
        message.info('当前文件未检测到英文内容')
        return
      }
      if (content.length > 10000) {
        message.info(`文件较长（${content.length} 字符），将分段翻译，请耐心等待...`)
      }
      const translated = await translateBodyByParagraph(content)
      if (translated) {
        currentFileContent.value = translated
        // 翻译产生的内容更新经 watch 流回 CodeEditor，因 suppressChange 屏蔽了 change 事件，
        // 这里需手动标记 dirty 以提示用户保存
        currentFileDirty.value = true
      }
      message.success('翻译完成')
      return
    }

    // 基础编辑模式
    // 仅翻译 displayName（name 为 kebab-case 标识符，禁止翻译）
    if (isEnglish(form.value.displayName)) {
      const res = await translateText(form.value.displayName)
      form.value.displayName = res.data || form.value.displayName
    }
    if (isEnglish(form.value.description)) {
      const res = await translateText(form.value.description)
      form.value.description = res.data || form.value.description
    }
    if (containsEnglish(body.value)) {
      const len = body.value.length
      if (len > 10000) {
        message.info(`正文较长（${len} 字符），将分段翻译，请耐心等待...`)
      }
      // 后端会对含中文字符的整段文本短路（直接返回原文不翻译），
      // 因此对中英混合正文需按段落拆分：仅纯英文段落发送翻译，含中文段落原样保留
      const translated = await translateBodyByParagraph(body.value)
      if (translated) body.value = translated
    }
    message.success('翻译完成')
  } catch (e) {
    message.error('翻译失败')
  } finally {
    isTranslating.value = false
  }
}

/**
 * 按段落（双换行）翻译正文：
 * - 含英文段落：调用后端翻译（由大模型决定哪些英文需要翻译）
 * - 纯中文段落 / 空白分隔符：原样保留
 * 段落结构（双换行）通过 split 捕获组保留，避免破坏 markdown 布局
 */
async function translateBodyByParagraph(text) {
  if (!text) return text
  // 捕获双换行（及以上）作为分隔符，使其原样保留在结果中
  const parts = text.split(/(\n{2,})/)
  const out = []
  for (const part of parts) {
    if (containsEnglish(part)) {
      try {
        const res = await translateText(part)
        out.push(res.data || part)
      } catch {
        out.push(part)
      }
    } else {
      out.push(part)
    }
  }
  return out.join('')
}

// ==================== IO 字段操作 ====================
function addField(list) {
  list.push({ name: '', type: 'string', description: '', required: false })
}

function removeField(list, index) {
  list.splice(index, 1)
}

// ==================== 数据加载 ====================
async function loadSkillDetail() {
  loading.value = true
  try {
    const res = await getSkillDetail(folderName)
    const data = res.data
    form.value = {
      name: data.name || '',
      displayName: data.displayName || '',
      description: data.description || '',
      canGenerateSkill: !!data.canGenerateSkill,
      inputs: (data.inputs || []).map(f => ({ ...f })),
      outputs: (data.outputs || []).map(f => ({ ...f }))
    }
    body.value = data.body || ''
    originalBody.value = body.value
    originalForm.value = JSON.parse(JSON.stringify(form.value))
  } catch (e) {
    message.error('加载技能详情失败')
  } finally {
    loading.value = false
  }
}

async function loadFileTree() {
  try {
    const res = await listSkillFiles(folderName)
    fileTree.value = res.data || []
  } catch (e) {
    // 加载失败静默处理
  }
}

// ==================== 高级模式文件操作 ====================
/**
 * 切换文件入口：
 *  - 当前文件有未保存修改 → 暂存目标动作，弹出「保存/丢弃/取消」对话框
 *  - 否则直接切换
 */
function handleFileSelect(path) {
  if (path === currentFilePath.value) return
  if (currentFileDirty.value && currentFilePath.value) {
    showUnsavedDialog(`当前文件「${currentFilePath.value}」`, () => doSwitchFile(path))
    return
  }
  doSwitchFile(path)
}

/** 真正执行文件切换：清空 dirty、读取目标文件内容到编辑器 */
async function doSwitchFile(path) {
  currentFilePath.value = path
  currentFileDirty.value = false
  try {
    const res = await readSkillFile(folderName, path)
    currentFileContent.value = res.data || ''
  } catch (e) {
    message.error('读取文件失败')
  }
}

// ==================== 未保存确认弹窗（通用） ====================
const unsavedDialog = ref({ show: false, contextText: '', pendingAction: null })

/** 基础模式是否含未保存修改（form 任一字段或 body 与原始值不一致） */
function isBasicDirty() {
  if (body.value !== originalBody.value) return true
  if (!originalForm.value) return false
  return JSON.stringify(originalForm.value) !== JSON.stringify(form.value)
}

function showUnsavedDialog(contextText, pendingAction) {
  unsavedDialog.value = { show: true, contextText, pendingAction }
}

async function saveAndProceed() {
  const action = unsavedDialog.value.pendingAction
  try {
    if (advancedMode.value && currentFileDirty.value) {
      await doSaveFile(currentFilePath.value, currentFileContent.value)
      currentFileDirty.value = false
    } else if (!advancedMode.value && isBasicDirty()) {
      await saveSkillMd(folderName, buildSaveData())
      originalBody.value = body.value
      originalForm.value = JSON.parse(JSON.stringify(form.value))
    }
    unsavedDialog.value.show = false
    if (action) await action()
  } catch (e) {
    message.error(e.response?.data?.message || '保存失败')
  }
}

function discardAndProceed() {
  const action = unsavedDialog.value.pendingAction
  unsavedDialog.value.show = false
  if (action) action()
}

function cancelProceed() {
  unsavedDialog.value.show = false
}

function handleFileContentChange(val) {
  currentFileContent.value = val
  currentFileDirty.value = true
}

async function handleCreateFile(path) {
  try {
    await createSkillFile(folderName, path, 'file')
    message.success('文件创建成功')
    await loadFileTree()
    // 自动选中新文件
    currentFilePath.value = path
    currentFileContent.value = ''
    currentFileDirty.value = false
  } catch (e) {
    message.error(e.response?.data?.message || '创建失败')
  }
}

async function handleCreateDir(path) {
  try {
    await createSkillFile(folderName, path, 'directory')
    message.success('目录创建成功')
    await loadFileTree()
  } catch (e) {
    message.error(e.response?.data?.message || '创建失败')
  }
}

async function handleDeleteFile(path) {
  try {
    await deleteSkillFile(folderName, path)
    message.success('删除成功')
    if (currentFilePath.value === path) {
      currentFilePath.value = ''
      currentFileContent.value = ''
      currentFileDirty.value = false
    }
    await loadFileTree()
  } catch (e) {
    message.error(e.response?.data?.message || '删除失败')
  }
}

// ==================== 保存 ====================
function buildSaveData() {
  return {
    name: form.value.name,
    displayName: form.value.displayName,
    description: form.value.description,
    canGenerateSkill: !!form.value.canGenerateSkill,
    inputs: form.value.inputs.filter(f => f.name.trim()),
    outputs: form.value.outputs.filter(f => f.name.trim()),
    body: body.value
  }
}

async function doSaveFile(path, content) {
  await saveSkillFile(folderName, path, content)
  currentFileDirty.value = false
}

async function handleSave() {
  if (saving.value) return
  if (!advancedMode.value && nameState.value.status === 'invalid') {
    message.error('技能名称规范化后仍非法（需含至少一个字母或数字）')
    return
  }
  saving.value = true
  try {
    if (advancedMode.value) {
      // 高级模式：保存当前编辑的文件
      if (currentFilePath.value) {
        await doSaveFile(currentFilePath.value, currentFileContent.value)
        message.success('文件保存成功')
      } else {
        message.warning('请先选择一个文件')
      }
    } else {
      // 基础模式：重建并保存 SKILL.md
      const res = await saveSkillMd(folderName, buildSaveData())
      originalBody.value = body.value
      originalForm.value = JSON.parse(JSON.stringify(form.value))
      // 处理可能的 folderName 变化（重命名）
      const payload = res.data || {}
      const newFolder = payload.folderName || folderName
      const renamed = String(payload.renamed) === 'true'
      if (renamed && newFolder !== folderName) {
        message.success(`保存成功，技能已重命名为 ${newFolder}`)
        // 替换 URL，避免下次操作命中旧 folderName
        router.replace(`/skill/edit/${newFolder}`)
      } else {
        message.success('保存成功')
      }
    }
    // 保存后刷新版本列表
    if (showVersionPanel.value) {
      await loadVersions()
    }
  } catch (e) {
    message.error(e.response?.data?.message || '保存失败')
  } finally {
    saving.value = false
  }
}

// ==================== 模式切换 ====================
async function toggleAdvancedMode() {
  if (!advancedMode.value) {
    // 基础 → 高级：若 form/body 有未保存修改，弹窗
    if (isBasicDirty()) {
      showUnsavedDialog('SKILL.md 元数据与正文', doEnterAdvancedMode)
      return
    }
    await doEnterAdvancedMode()
  } else {
    // 高级 → 基础：若当前文件 dirty，弹窗
    if (currentFileDirty.value && currentFilePath.value) {
      showUnsavedDialog(`当前文件「${currentFilePath.value}」`, doExitAdvancedMode)
      return
    }
    doExitAdvancedMode()
  }
}

async function doEnterAdvancedMode() {
  await loadFileTree()
  // 默认选中 SKILL.md
  currentFilePath.value = 'SKILL.md'
  currentFileContent.value = ''
  currentFileDirty.value = false
  try {
    const res = await readSkillFile(folderName, 'SKILL.md')
    currentFileContent.value = res.data || ''
  } catch (e) {
    // 读取 SKILL.md 失败静默处理
  }
  advancedMode.value = true
}

function doExitAdvancedMode() {
  advancedMode.value = false
}

function goBack() {
  router.push('/management')
}

// ==================== 版本历史 ====================
async function loadVersions() {
  versionsLoading.value = true
  try {
    const res = await listVersions(folderName)
    versions.value = (res.data || []).reverse() // 最新的排在前面
  } catch (e) {
    message.error('加载版本历史失败')
  } finally {
    versionsLoading.value = false
  }
}

async function toggleVersionPanel() {
  showVersionPanel.value = !showVersionPanel.value
  if (showVersionPanel.value) {
    await loadVersions()
  } else {
    // 关闭时重置状态
    selectedVersion.value = null
    versionContent.value = ''
    diffVersion.value = null
    diffContent.value = ''
  }
}

async function handleVersionSelect(v) {
  selectedVersion.value = v
  diffVersion.value = null
  diffContent.value = ''
  try {
    const res = await getVersionContent(folderName, v.number)
    versionContent.value = res.data || ''
  } catch (e) {
    message.error('读取版本内容失败')
  }
}

async function handleDiffSelect(v) {
  diffVersion.value = v
  try {
    const res = await getVersionContent(folderName, v.number)
    diffContent.value = res.data || ''
  } catch (e) {
    message.error('读取版本内容失败')
  }
}

function handleRollback(v) {
  dialog.warning({
    title: '确认回滚',
    content: `确定要回滚到 v${v.number} 吗？当前工作区内容将被替换。`,
    maskClosable: false,
    positiveText: '确认回滚',
    negativeText: '取消',
    onPositiveClick: async () => {
      try {
        await rollbackVersion(folderName, v.number)
        message.success('回滚成功')
        // 重新加载技能详情
        await loadSkillDetail()
        if (advancedMode.value) {
          await loadFileTree()
        }
        await loadVersions()
        // 重置版本查看状态
        selectedVersion.value = null
        versionContent.value = ''
        diffVersion.value = null
        diffContent.value = ''
      } catch (e) {
        message.error('回滚失败')
      }
    }
  })
}

function startEditMessage(v) {
  editingMessage.value = v.number
  editingMessageText.value = v.message || ''
}

async function saveMessage(v) {
  try {
    await updateVersionMessage(folderName, v.number, editingMessageText.value)
    v.message = editingMessageText.value
    editingMessage.value = false
    message.success('备注已更新')
  } catch (e) {
    message.error('更新备注失败')
  }
}

function cancelEditMessage() {
  editingMessage.value = false
}

function formatTime(dateStr) {
  if (!dateStr) return ''
  const d = new Date(dateStr)
  const pad = n => String(n).padStart(2, '0')
  return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
}

/** 简单的行级 diff：返回 [{type: 'same'|'add'|'del', text}] */
function computeDiff(oldText, newText) {
  const oldLines = (oldText || '').split('\n')
  const newLines = (newText || '').split('\n')

  // LCS-based diff 简化版：逐行对比
  const m = oldLines.length
  const n = newLines.length

  // 使用简单的双序列对比（对小文件足够）
  const result = []
  const lcs = Array.from({ length: m + 1 }, () => new Uint16Array(n + 1))

  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (oldLines[i - 1] === newLines[j - 1]) {
        lcs[i][j] = lcs[i - 1][j - 1] + 1
      } else {
        lcs[i][j] = Math.max(lcs[i - 1][j], lcs[i][j - 1])
      }
    }
  }

  // 回溯
  const ops = []
  let i = m, j = n
  while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
      ops.unshift({ type: 'same', text: oldLines[i - 1] })
      i--; j--
    } else if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {
      ops.unshift({ type: 'add', text: newLines[j - 1] })
      j--
    } else {
      ops.unshift({ type: 'del', text: oldLines[i - 1] })
      i--
    }
  }
  return ops
}

const diffResult = computed(() => {
  if (!diffVersion.value || !selectedVersion.value) return []
  const oldText = diffVersion.value.number < selectedVersion.value.number
    ? diffContent.value
    : versionContent.value
  const newText = diffVersion.value.number < selectedVersion.value.number
    ? versionContent.value
    : diffContent.value
  return computeDiff(oldText, newText)
})

// ==================== 生命周期 ====================
onMounted(() => {
  loadSkillDetail()
})
</script>

<template>
  <div class="skill-edit-page">
    <!-- 顶部工具栏 -->
    <div class="edit-toolbar">
      <div class="toolbar-left">
        <n-button quaternary @click="goBack" class="back-btn">
          <template #icon><n-icon>
              <ArrowBackOutline />
            </n-icon></template>
          返回
        </n-button>
        <span class="edit-title">{{ form.displayName || form.name || folderName }}</span>
      </div>
      <div class="toolbar-right">
        <n-tooltip v-if="hasEnglishContent" placement="bottom">
          <template #trigger>
            <n-button :loading="isTranslating" @click="handleTranslate" quaternary>
              <template #icon><n-icon size="16">
                  <GlobeOutline />
                </n-icon></template>
              翻译
            </n-button>
          </template>
          {{ advancedMode ? '翻译当前文件为中文' : '检测到英文内容，点击翻译为中文' }}
        </n-tooltip>

        <n-button quaternary @click="toggleVersionPanel" :type="showVersionPanel ? 'primary' : 'default'">
          <template #icon><n-icon>
              <TimeOutline />
            </n-icon></template>
          版本历史
        </n-button>

        <n-button :type="advancedMode ? 'primary' : 'default'" quaternary @click="toggleAdvancedMode">
          <template #icon><n-icon>
              <CodeSlashOutline />
            </n-icon></template>
          {{ advancedMode ? '基础编辑' : '高级编辑' }}
        </n-button>

        <n-button type="primary" @click="handleSave" :loading="saving">
          <template #icon><n-icon>
              <SaveOutline />
            </n-icon></template>
          保存
        </n-button>
      </div>
    </div>

    <div class="edit-body">
      <!-- 主编辑区域 -->
      <div class="main-content">
        <!-- 加载中 -->
        <div v-if="loading" class="loading-area">
          <n-spin size="large" />
        </div>

        <!-- ==================== 基础编辑模式 ==================== -->
        <div v-else-if="!advancedMode" class="basic-editor">
          <!-- 元数据表单 -->
          <div class="meta-section">
            <div class="form-row">
              <div class="form-group">
                <label class="form-label">
                  技能展示名称
                  <span class="label-hint">（可包含中文）</span>
                </label>
                <n-input v-model:value="form.displayName" placeholder="如：军事工作流技能生成器" :maxlength="100" />
              </div>
              <div class="form-group" style="flex: 2;">
                <label class="form-label">技能描述</label>
                <n-input v-model:value="form.description" placeholder="请输入技能描述" :maxlength="500" />
              </div>
            </div>

            <div class="form-group">
              <label class="form-label">
                技能名称
                <span class="label-hint">（kebab-case，唯一标识，禁用大写/空格/下划线/中文）</span>
              </label>
              <n-input v-model:value="form.name" placeholder="如：military-workflow-skill-creator" :maxlength="100" />
              <div v-if="nameState.status === 'will-rename'" class="name-hint name-warn">
                ⚠ 保存时将重命名目录：<code>{{ folderName }}</code> → <code>{{ nameState.normalized }}</code>
                <span class="rename-side-effects">（标签 / 分类 / 工作流引用将自动迁移）</span>
              </div>
              <div v-else-if="nameState.status === 'will-normalize'" class="name-hint name-warn">
                保存时将自动规范化为：<code>{{ nameState.normalized }}</code>
              </div>
              <div v-else-if="nameState.status === 'invalid'" class="name-hint name-error">
                规范化后仍非法：需含至少一个字母或数字
              </div>
              <div v-else-if="nameState.status === 'valid'" class="name-hint name-ok">
                ✓ 合法 kebab-case
              </div>
            </div>

            <div class="form-group">
              <n-checkbox v-model:checked="form.canGenerateSkill">
                允许此技能生成其他技能（在「技能生成」菜单中可选）
              </n-checkbox>
            </div>

            <!-- 输入字段 -->
            <div class="form-group">
              <div class="section-header">
                <label class="form-label">输入字段</label>
                <n-button size="tiny" quaternary @click="addField(form.inputs)">
                  <template #icon><n-icon size="14">+</n-icon></template>
                  添加
                </n-button>
              </div>
              <div v-if="form.inputs.length" class="io-list">
                <div v-for="(field, i) in form.inputs" :key="'in-' + i" class="io-row">
                  <n-input v-model:value="field.name" placeholder="字段名" size="small" style="width: 100px;" />
                  <n-select v-model:value="field.type" :options="IO_TYPES" size="small" style="width: 110px;" />
                  <n-input v-model:value="field.description" placeholder="描述" size="small" style="flex: 1;" />
                  <n-checkbox v-model:checked="field.required" size="small">必填</n-checkbox>
                  <button class="remove-btn" @click="removeField(form.inputs, i)">✕</button>
                </div>
              </div>
              <div v-else class="io-empty">暂无输入字段</div>
            </div>

            <!-- 输出字段 -->
            <div class="form-group">
              <div class="section-header">
                <label class="form-label">输出字段</label>
                <n-button size="tiny" quaternary @click="addField(form.outputs)">
                  <template #icon><n-icon size="14">+</n-icon></template>
                  添加
                </n-button>
              </div>
              <div v-if="form.outputs.length" class="io-list">
                <div v-for="(field, i) in form.outputs" :key="'out-' + i" class="io-row">
                  <n-input v-model:value="field.name" placeholder="字段名" size="small" style="width: 100px;" />
                  <n-select v-model:value="field.type" :options="IO_TYPES" size="small" style="width: 110px;" />
                  <n-input v-model:value="field.description" placeholder="描述" size="small" style="flex: 1;" />
                  <n-checkbox v-model:checked="field.required" size="small">必填</n-checkbox>
                  <button class="remove-btn" @click="removeField(form.outputs, i)">✕</button>
                </div>
              </div>
              <div v-else class="io-empty">暂无输出字段</div>
            </div>
          </div>

          <!-- 正文编辑器 -->
          <div class="body-section">
            <label class="form-label" style="margin-bottom: 8px;">正文内容 (Markdown)</label>
            <CodeEditor v-model="body" language="markdown" :height="'calc(100vh - 520px)'" :minimap="false" />
          </div>
        </div>

        <!-- ==================== 高级编辑模式 ==================== -->
        <div v-else class="advanced-editor">
          <!-- 文件树 -->
          <div class="file-tree-panel">
            <FileTree :files="fileTree" :currentFile="currentFilePath" @select="handleFileSelect"
              @create-file="handleCreateFile" @create-dir="handleCreateDir" @delete="handleDeleteFile" />
          </div>

          <!-- 代码编辑器 -->
          <div class="editor-panel">
            <div v-if="currentFilePath" class="editor-header">
              <span class="file-path">{{ currentFilePath }}</span>
              <span v-if="currentFileDirty" class="dirty-indicator">●</span>
            </div>
            <CodeEditor v-if="currentFilePath" v-model="currentFileContent"
              :language="getLanguageFromPath(currentFilePath)" :height="'calc(100vh - 140px)'" :minimap="true"
              @change="handleFileContentChange" />
            <div v-else class="no-file-selected">
              <p>选择文件以编辑</p>
            </div>
          </div>
        </div>
      </div>

      <!-- ==================== 版本历史面板 ==================== -->
      <transition name="slide">
        <div v-if="showVersionPanel" class="version-panel">
          <div class="vp-header">
            <h3 class="vp-title">版本历史</h3>
            <n-button quaternary size="tiny" @click="toggleVersionPanel">✕</n-button>
          </div>

          <!-- 版本列表 -->
          <div class="vp-list">
            <n-spin v-if="versionsLoading" size="small" />
            <n-empty v-else-if="!versions.length" description="暂无版本" size="small" />
            <div v-else class="version-items">
              <div v-for="v in versions" :key="v.number" class="version-item" :class="{
          active: selectedVersion?.number === v.number,
          'diff-selected': diffVersion?.number === v.number
        }">
                <div class="vi-main" @click="handleVersionSelect(v)">
                  <div class="vi-row">
                    <span class="vi-version">v{{ v.number }}<span v-if="v.current" class="vi-current">当前</span></span>
                    <span class="vi-time">{{ formatTime(v.createdAt) }}</span>
                  </div>
                  <div v-if="editingMessage !== v.number" class="vi-message" @dblclick="startEditMessage(v)">
                    {{ v.message || '双击编辑备注' }}
                  </div>
                  <div v-else class="vi-message-edit">
                    <n-input v-model:value="editingMessageText" size="tiny" placeholder="版本备注"
                      @keyup.enter="saveMessage(v)" @keyup.escape="cancelEditMessage" />
                    <n-button size="tiny" type="primary" @click="saveMessage(v)">✓</n-button>
                    <n-button size="tiny" @click="cancelEditMessage">✕</n-button>
                  </div>
                </div>
                <div class="vi-actions">
                  <button v-if="selectedVersion && selectedVersion.number !== v.number" class="vi-action-btn diff-btn"
                    @click.stop="handleDiffSelect(v)" title="与选中版本对比">⟷</button>
                  <button v-if="!v.current" class="vi-action-btn rollback-btn" @click.stop="handleRollback(v)"
                    title="回滚到此版本">↩</button>
                </div>
              </div>
            </div>
          </div>

          <!-- 版本内容 / Diff 对比 -->
          <div v-if="selectedVersion" class="vp-content">
            <div class="vp-content-header">
              <span v-if="!diffVersion">
                v{{ selectedVersion.number }} 的内容
              </span>
              <span v-else>
                v{{ Math.min(selectedVersion.number, diffVersion.number) }}
                →
                v{{ Math.max(selectedVersion.number, diffVersion.number) }}
                变更对比
              </span>
              <n-button v-if="diffVersion" size="tiny" quaternary @click="diffVersion = null; diffContent = ''">
                退出对比
              </n-button>
            </div>

            <!-- Diff 视图 -->
            <div v-if="diffVersion" class="diff-view">
              <div v-for="(line, idx) in diffResult" :key="idx" class="diff-line" :class="'diff-' + line.type">
                <span class="diff-prefix">{{ line.type === 'add' ? '+' : line.type === 'del' ? '-' : ' ' }}</span>
                <span class="diff-text">{{ line.text }}</span>
              </div>
            </div>

            <!-- 单版本内容 -->
            <div v-else class="version-content-view">
              <pre>{{ versionContent }}</pre>
            </div>
          </div>
        </div>
      </transition>
    </div>

    <!-- 未保存确认（切换文件 / 切换模式共用） -->
    <n-modal v-model:show="unsavedDialog.show" preset="dialog" title="当前修改未保存" :show-icon="false"
      :mask-closable="false">
      <span>
        {{ unsavedDialog.contextText }} 有未保存的修改，是否保存？
      </span>
      <template #action>
        <n-button @click="cancelProceed">取消</n-button>
        <n-button quaternary type="error" @click="discardAndProceed">丢弃</n-button>
        <n-button type="primary" @click="saveAndProceed">保存</n-button>
      </template>
    </n-modal>
  </div>
</template>

<style scoped>
.skill-edit-page {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.edit-body {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.main-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* ==================== 工具栏 ==================== */
.edit-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 24px;
  border-bottom: 1px solid var(--border-color);
  background: var(--bg-secondary);
  flex-shrink: 0;
}

.toolbar-left {
  display: flex;
  align-items: center;
  gap: 12px;
}

.toolbar-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.back-btn {
  color: var(--text-secondary);
}

.edit-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--text-primary);
}

/* ==================== 加载 ==================== */
.loading-area {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ==================== 基础编辑模式 ==================== */
.basic-editor {
  flex: 1;
  overflow-y: auto;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.meta-section {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.form-row {
  display: flex;
  gap: 16px;
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}

.form-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
}

.label-hint {
  font-size: 11px;
  font-weight: 400;
  color: var(--text-tertiary);
}

.name-hint {
  font-size: 12px;
  margin-top: 2px;
}

.name-hint code {
  background: rgba(255, 255, 255, 0.06);
  padding: 1px 6px;
  border-radius: 3px;
  font-family: 'Consolas', 'Monaco', monospace;
}

.rename-side-effects {
  color: var(--text-tertiary);
  margin-left: 4px;
}

.name-warn {
  color: #e0a526;
}

.name-error {
  color: #ef4444;
}

.name-ok {
  color: #22c55e;
}

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.io-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.io-row {
  display: flex;
  align-items: center;
  gap: 6px;
}

.io-empty {
  font-size: 12px;
  color: var(--text-tertiary);
  padding: 6px 0;
}

.remove-btn {
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  border-radius: 4px;
  color: var(--text-tertiary);
  cursor: pointer;
  font-size: 12px;
  flex-shrink: 0;
}

.remove-btn:hover {
  background: rgba(255, 80, 80, 0.1);
  color: #ff5050;
}

.body-section {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 300px;
}

/* ==================== 高级编辑模式 ==================== */
.advanced-editor {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.file-tree-panel {
  width: 240px;
  border-right: 1px solid var(--border-color);
  background: var(--bg-secondary);
  flex-shrink: 0;
}

.editor-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.editor-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border-color);
  background: var(--bg-secondary);
  flex-shrink: 0;
}

.file-path {
  font-size: 13px;
  color: var(--text-secondary);
  font-family: monospace;
}

.dirty-indicator {
  color: #e0a526;
  font-size: 14px;
}

.no-file-selected {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 14px;
}

/* ==================== 版本历史面板 ==================== */
.version-panel {
  width: 400px;
  border-left: 1px solid var(--border-color);
  background: var(--bg-secondary);
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  overflow: hidden;
}

.vp-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-color);
}

.vp-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}

.vp-list {
  flex-shrink: 0;
  max-height: 280px;
  overflow-y: auto;
  padding: 8px;
}

.version-items {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.version-item {
  display: flex;
  align-items: stretch;
  border-radius: 6px;
  border: 1px solid transparent;
  transition: all 0.15s;
  cursor: pointer;
}

.version-item:hover {
  background: rgba(37, 99, 235, 0.06);
}

.version-item.active {
  background: rgba(37, 99, 235, 0.1);
  border-color: rgba(37, 99, 235, 0.3);
}

.version-item.diff-selected {
  background: rgba(245, 158, 11, 0.1);
  border-color: rgba(245, 158, 11, 0.3);
}

.vi-main {
  flex: 1;
  padding: 8px 10px;
  min-width: 0;
}

.vi-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.vi-version {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-accent);
}

.vi-current {
  font-size: 10px;
  font-weight: 500;
  background: rgba(34, 197, 94, 0.15);
  color: #22c55e;
  padding: 0 4px;
  border-radius: 3px;
  margin-left: 4px;
  vertical-align: middle;
}

.vi-time {
  font-size: 11px;
  color: var(--text-tertiary);
}

.vi-message {
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.vi-message-edit {
  display: flex;
  gap: 4px;
  margin-top: 4px;
  align-items: center;
}

.vi-actions {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding: 4px 6px;
  opacity: 0;
  transition: opacity 0.15s;
}

.version-item:hover .vi-actions {
  opacity: 1;
}

.vi-action-btn {
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 13px;
  background: transparent;
  transition: background 0.15s;
}

.diff-btn {
  color: var(--color-primary);
}

.diff-btn:hover {
  background: rgba(37, 99, 235, 0.15);
}

.rollback-btn {
  color: var(--color-danger);
}

.rollback-btn:hover {
  background: rgba(239, 68, 68, 0.15);
}

/* 版本内容区域 */
.vp-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-top: 1px solid var(--border-color);
}

.vp-content-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 16px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border-color);
  flex-shrink: 0;
}

.version-content-view {
  flex: 1;
  overflow: auto;
  padding: 12px 16px;
}

.version-content-view pre {
  font-size: 12px;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-all;
  color: var(--text-secondary);
  margin: 0;
  font-family: 'Consolas', 'Monaco', monospace;
}

/* Diff 视图 */
.diff-view {
  flex: 1;
  overflow: auto;
  padding: 4px 0;
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 12px;
  line-height: 1.6;
}

.diff-line {
  display: flex;
  min-height: 20px;
}

.diff-prefix {
  width: 20px;
  text-align: center;
  color: var(--text-tertiary);
  flex-shrink: 0;
  user-select: none;
}

.diff-text {
  white-space: pre-wrap;
  word-break: break-all;
  padding-right: 8px;
}

.diff-same {
  color: var(--text-secondary);
}

.diff-add {
  background: rgba(34, 197, 94, 0.12);
}

.diff-add .diff-prefix,
.diff-add .diff-text {
  color: #22c55e;
}

.diff-del {
  background: rgba(239, 68, 68, 0.12);
}

.diff-del .diff-prefix,
.diff-del .diff-text {
  color: #ef4444;
}

/* 面板滑入动画 */
.slide-enter-active,
.slide-leave-active {
  transition: transform 0.25s ease, opacity 0.25s ease;
}

.slide-enter-from,
.slide-leave-to {
  transform: translateX(100%);
  opacity: 0;
}
</style>
