<template>
  <div class="api-skill-page">
    <div class="api-skill-header">
      <h1>UAP应用接口skill</h1>
      <p class="lead">
        在 Cursor 等编辑器中为本仓库或业务项目提供 <strong>UAP 接口对接</strong> 相关的助手能力：下载下方 Skill 文件，按说明放入技能目录即可。
      </p>
    </div>

    <el-card shadow="never" class="block-card">
      <template #header>
        <span>这是什么</span>
      </template>
      <p>
        Cursor <strong>Skill</strong> 是一段给 AI 读的说明（Markdown），用于在对话里约束「如何调用统一认证平台 API、签名规则、安全注意点」等。
        它与站内「接口开发文档」一致互补：文档偏完整图文，Skill 偏给自动化助手用的精简规则。
      </p>
    </el-card>

    <el-card shadow="never" class="block-card">
      <template #header>
        <span>安装步骤（Cursor）</span>
      </template>
      <ol class="steps">
        <li>
          点击下方按钮下载 <code>{{ zipFileName }}</code>（内含 <code>SKILL.md</code>、<code>LLM_UAP_INTEGRATION.md</code>、<code>VERSION</code>、<code>CHANGELOG.md</code>；前两份缺一则 AI 无法读到接口细节）。
        </li>
        <li>
          选择一个技能目录（二选一）：
          <ul class="sub-list">
            <li><strong>项目级</strong>：<code>&lt;仓库根&gt;/.cursor/skills/</code> —— 仅当前项目可用，会随仓库共享。</li>
            <li><strong>用户级</strong>：<code>~/.cursor/skills/</code>（Windows 为 <code>%USERPROFILE%\.cursor\skills\</code>）—— 本机所有项目都可用。</li>
          </ul>
        </li>
        <li>把 zip 解压到上一步选择的目录，最终路径形如 <code>.../.cursor/skills/uap-api/SKILL.md</code> 与 <code>.../.cursor/skills/uap-api/LLM_UAP_INTEGRATION.md</code>。</li>
        <li>重启 Cursor 或重新打开项目后，在对话中说明「按 UAP skill 对接」即可触发相关约束。</li>
      </ol>
      <p class="hint">
        具体目录名以 Cursor 当前版本的 <a href="https://cursor.com/docs" target="_blank" rel="noopener noreferrer">官方文档</a> 为准；不同版本可能使用 <code>skills</code> 或规则目录，请与本地设置对照。
      </p>
    </el-card>

    <el-card shadow="never" class="block-card">
      <template #header>
        <span>下载</span>
      </template>
      <div class="download-row">
        <el-button type="primary" :loading="zipping" @click="downloadZip">
          <el-icon class="btn-icon"><Download /></el-icon>
          {{ zipping ? '正在打包…' : `下载 ${zipFileName}（含 Skill 与修订记录）` }}
        </el-button>
      </div>
      <el-alert
        v-if="zipError"
        type="error"
        :closable="false"
        show-icon
        class="zip-error"
        :title="zipError"
      />
      <p class="hint">
        zip 解压后得到 <code>uap-api/</code> 目录，直接放入 <code>.cursor/skills/</code> 即可。<code>SKILL.md</code> 对 <code>LLM_UAP_INTEGRATION.md</code> 的引用为相对路径。
      </p>
      <details class="fallback">
        <summary>无法打包？分开下载</summary>
        <div class="download-row fallback-row">
          <a :href="skillUrl" download="SKILL.md" class="download-anchor">
            <el-button size="small">下载 SKILL.md</el-button>
          </a>
          <a :href="referenceUrl" download="LLM_UAP_INTEGRATION.md" class="download-anchor">
            <el-button size="small">下载 LLM_UAP_INTEGRATION.md</el-button>
          </a>
        </div>
        <div class="path-list">
          <div class="file-path">静态路径：{{ skillUrl }}</div>
          <div class="file-path">静态路径：{{ referenceUrl }}</div>
        </div>
        <p class="hint">两份 Skill 文档必须放在同一目录（目录名 <code>uap-api</code>）。</p>
      </details>
    </el-card>

    <el-card shadow="never" class="block-card">
      <template #header>
        <span>修订历史（当前包版本 {{ currentSkillVersion }}）</span>
      </template>
      <p class="changelog-intro">
        本站打包的 zip 文件名与包内 <code>VERSION</code> 与此处<strong>当前版本</strong>一致；升级 Skill 时请对照下列记录。
      </p>
      <table class="changelog-table">
        <thead>
          <tr>
            <th scope="col">版本</th>
            <th scope="col">日期</th>
            <th scope="col">说明</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="row in skillRevisionHistory" :key="row.version">
            <td class="cell-version">{{ row.version }}</td>
            <td class="cell-date">{{ row.date }}</td>
            <td>
              <ul class="changelog-items">
                <li v-for="(item, i) in row.changes" :key="`${row.version}-${i}`">{{ item }}</li>
              </ul>
            </td>
          </tr>
        </tbody>
      </table>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import JSZip from 'jszip'
import { Download } from '@element-plus/icons-vue'

/** 与下列修订史第一条一致；发版时同步更新 skillRevisionHistory */
const UAP_API_SKILL_VERSION = '1.0.1'

interface SkillRevisionRow {
  version: string
  date: string
  changes: string[]
}

/** 时间倒序：新版本插在数组开头 */
const skillRevisionHistory: SkillRevisionRow[] = [
  {
    version: '1.0.1',
    date: '2026-05-13',
    changes: [
      '补充 M2M：GET /users/m2m/by-platform-id/{user_id}（X-App-Access-Token 按平台用户主键查询档案）；SKILL.md 与 LLM_UAP_INTEGRATION.md §7.2.1 等同步更新',
      '下载页增加修订历史；zip 文件名带版本号，包内增加 VERSION、CHANGELOG.md',
    ],
  },
]

const currentSkillVersion = UAP_API_SKILL_VERSION

const zipFileName = computed(() => `uap-api-${UAP_API_SKILL_VERSION}.zip`)

const skillUrl = '/skills/uap-api/SKILL.md'
const referenceUrl = '/skills/uap-api/LLM_UAP_INTEGRATION.md'

const zipping = ref(false)
const zipError = ref('')

function buildPackagedChangelogMd(): string {
  const header = `# uap-api Skill 修订记录\n\n当前包版本：${UAP_API_SKILL_VERSION}\n\n`
  const body = skillRevisionHistory
    .map(
      (row) =>
        `## ${row.version}（${row.date}）\n\n${row.changes.map((c) => `- ${c}`).join('\n')}`,
    )
    .join('\n\n')
  return header + body + '\n'
}

async function fetchText(url: string): Promise<string> {
  const resp = await fetch(url, { cache: 'no-cache' })
  if (!resp.ok) {
    throw new Error(`下载 ${url} 失败：HTTP ${resp.status}`)
  }
  return await resp.text()
}

async function downloadZip() {
  if (zipping.value) return
  zipping.value = true
  zipError.value = ''
  try {
    const [skillText, referenceText] = await Promise.all([
      fetchText(skillUrl),
      fetchText(referenceUrl),
    ])
    const zip = new JSZip()
    const folder = zip.folder('uap-api')
    if (!folder) throw new Error('创建 zip 目录失败')
    folder.file('SKILL.md', skillText)
    folder.file('LLM_UAP_INTEGRATION.md', referenceText)
    folder.file('VERSION', `${UAP_API_SKILL_VERSION}\n`)
    folder.file('CHANGELOG.md', buildPackagedChangelogMd())
    const blob = await zip.generateAsync({ type: 'blob' })
    const href = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = href
    a.download = zipFileName.value
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(href)
  } catch (err) {
    zipError.value = err instanceof Error ? err.message : '打包失败，请稍后重试或使用下方分开下载。'
  } finally {
    zipping.value = false
  }
}
</script>

<style scoped>
.api-skill-page {
  padding: 30px;
  max-width: 900px;
  margin: 0 auto;
  background-color: #fff;
  border-radius: 8px;
  min-height: 80vh;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

.api-skill-header {
  border-bottom: 1px solid #eaecef;
  margin-bottom: 24px;
  padding-bottom: 16px;
}

h1 {
  font-size: 28px;
  color: #1f2f3d;
  font-weight: 600;
  margin: 0 0 12px;
}

.lead {
  margin: 0;
  color: #606266;
  line-height: 1.6;
  font-size: 15px;
}

.block-card {
  margin-bottom: 16px;
}

.block-card :deep(.el-card__body) {
  color: #606266;
  line-height: 1.7;
  font-size: 14px;
}

.steps {
  margin: 0 0 12px;
  padding-left: 20px;
}

.steps li {
  margin-bottom: 8px;
}

.hint {
  margin: 0;
  font-size: 13px;
  color: #909399;
}

.download-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

.btn-icon {
  margin-right: 6px;
  vertical-align: middle;
}

.download-anchor {
  text-decoration: none;
  display: inline-flex;
}

.path-list {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.file-path {
  font-size: 13px;
  color: #909399;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  word-break: break-all;
}

.sub-list {
  margin: 6px 0 0;
  padding-left: 20px;
}

.sub-list li {
  margin-bottom: 4px;
}

.zip-error {
  margin: 12px 0;
}

.fallback {
  margin-top: 16px;
  padding: 10px 12px;
  border: 1px dashed #e4e7ed;
  border-radius: 4px;
  background: #fafafa;
}

.fallback > summary {
  cursor: pointer;
  color: #606266;
  font-size: 13px;
  user-select: none;
}

.fallback[open] > summary {
  margin-bottom: 8px;
}

.fallback-row {
  margin-top: 4px;
}

code {
  font-size: 13px;
  background: #f5f7fa;
  padding: 2px 6px;
  border-radius: 4px;
}

.changelog-intro {
  margin: 0 0 16px;
  font-size: 14px;
  color: #606266;
}

.changelog-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.changelog-table th,
.changelog-table td {
  border: 1px solid #ebeef5;
  padding: 10px 12px;
  vertical-align: top;
  text-align: left;
}

.changelog-table th {
  background: #f5f7fa;
  color: #303133;
  font-weight: 600;
}

.cell-version {
  white-space: nowrap;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-weight: 500;
}

.cell-date {
  white-space: nowrap;
  color: #909399;
}

.changelog-items {
  margin: 0;
  padding-left: 18px;
}

.changelog-items li {
  margin-bottom: 6px;
}

.changelog-items li:last-child {
  margin-bottom: 0;
}
</style>
