<script setup>
// 后台：桌面端版本管理 — 上传安装包（从文件名解析版本）、改更新说明、只保留最近 3 个包
import { ref, onMounted, computed } from 'vue'
import http, { toast, serverBase } from '../../api/http'
import NavIcon from '../../components/NavIcon.vue'

const data = ref(null)
const notes = ref('')
const force = ref(false)
const saving = ref(false)
const uploading = ref(false)
const fileInput = ref(null)
const pickedName = ref('')
const progress = ref(0)

const releases = computed(() => data.value?.releases || [])

function fmtSize(n) {
  if (!n && n !== 0) return '-'
  if (n < 1024) return n + ' B'
  if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB'
  return (n / 1024 / 1024).toFixed(1) + ' MB'
}

function fmtTime(ts) {
  if (!ts) return '-'
  const d = new Date(ts * 1000)
  const p = (x) => String(x).padStart(2, '0')
  return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
}

function absURL(u) {
  if (!u) return ''
  if (/^https?:\/\//i.test(u)) return u
  const base = serverBase() || (typeof location !== 'undefined' ? location.origin : '')
  return base.replace(/\/+$/, '') + (u.startsWith('/') ? u : '/' + u)
}

async function load() {
  data.value = await http.get('/admin/version')
  notes.value = data.value.release_notes || ''
  force.value = Number(data.value.force) === 1
}

async function saveMeta() {
  saving.value = true
  try {
    data.value = await http.put('/admin/version', {
      release_notes: notes.value,
      force: force.value ? 1 : 0,
    })
    toast('已保存', 'success')
  } finally {
    saving.value = false
  }
}

function onPick(e) {
  const f = e.target.files?.[0]
  pickedName.value = f ? f.name : ''
}

async function upload() {
  const f = fileInput.value?.files?.[0]
  if (!f) {
    toast('请先选择安装包')
    return
  }
  uploading.value = true
  progress.value = 0
  try {
    const fd = new FormData()
    fd.append('file', f)
    fd.append('release_notes', notes.value || '')
    fd.append('force', force.value ? '1' : '0')
    data.value = await http.post('/admin/version/upload', fd, {
      timeout: 300000,
      onUploadProgress: (ev) => {
        if (ev.total) progress.value = Math.round((ev.loaded / ev.total) * 100)
      },
    })
    notes.value = data.value.release_notes || ''
    force.value = Number(data.value.force) === 1
    pickedName.value = ''
    if (fileInput.value) fileInput.value.value = ''
    toast(`已发布 v${data.value.version}，自动保留最近 ${data.value.keep} 个包`, 'success')
  } finally {
    uploading.value = false
    progress.value = 0
  }
}

async function remove(item) {
  if (!confirm(`确定删除 ${item.filename}？`)) return
  data.value = await http.delete('/admin/version/' + encodeURIComponent(item.filename))
  toast('已删除', 'success')
}

onMounted(load)
</script>

<template>
  <div v-if="data">
    <h2 class="page-title">桌面版本</h2>
    <div class="panel intro">
      <p>
        上传 Electron 安装包（如 <code>PixelArcade-Setup-1.0.1.exe</code>），系统从<strong>文件名</strong>解析版本号，
        写入公开接口 <code>/api/version/latest</code>，桌面端「检查更新」会在应用内下载并启动安装程序。
        安装包地址为 <code>/api/download/…</code>（走已有 API 反代）。磁盘上<strong>只保留最近 {{ data.keep }} 个</strong>安装包。
      </p>
    </div>

    <div class="panel cur">
      <h3 class="sec-head"><NavIcon name="download" :size="16" /> 当前对外版本</h3>
      <div class="cur-row">
        <div class="kv"><span class="k">版本号</span><b class="v">{{ data.version || '-' }}</b></div>
        <div class="kv grow">
          <span class="k">下载地址</span>
          <a v-if="data.download_url" class="v link" :href="absURL(data.download_url)" target="_blank" rel="noopener">{{ absURL(data.download_url) }}</a>
          <span v-else class="v text-dim">尚未上传</span>
        </div>
      </div>
      <label class="field">
        <span>更新说明</span>
        <textarea v-model="notes" rows="2" placeholder="一行简短文案，桌面端更新弹窗展示" />
      </label>
      <label class="force">
        <input v-model="force" type="checkbox" />
        强制更新（桌面端不允许跳过）
      </label>
      <div class="actions">
        <button class="btn primary" :disabled="saving" @click="saveMeta">
          <NavIcon name="save" :size="15" /> {{ saving ? '保存中…' : '保存说明' }}
        </button>
      </div>
    </div>

    <div class="panel upload">
      <h3 class="sec-head"><NavIcon name="upload" :size="16" /> 上传安装包</h3>
      <div class="pick">
        <input ref="fileInput" type="file" accept=".exe,.msi,.dmg,.zip,.7z" @change="onPick" />
        <span class="picked text-dim">{{ pickedName || '未选择文件' }}</span>
      </div>
      <div v-if="uploading" class="bar"><i :style="{ width: progress + '%' }" /></div>
      <div class="actions">
        <button class="btn primary" :disabled="uploading || !pickedName" @click="upload">
          {{ uploading ? `上传中 ${progress}%` : '上传并发布' }}
        </button>
      </div>
    </div>

    <div class="panel list">
      <h3 class="sec-head">已保留的安装包（{{ releases.length }} / {{ data.keep }}）</h3>
      <table v-if="releases.length" class="tbl">
        <thead>
          <tr>
            <th>版本</th>
            <th>文件名</th>
            <th>大小</th>
            <th>上传时间</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="r in releases" :key="r.filename" :class="{ cur: r.is_current }">
            <td>
              <b>v{{ r.version || '?' }}</b>
              <span v-if="r.is_current" class="badge">当前</span>
            </td>
            <td class="fn">
              <a :href="absURL(r.url)" target="_blank" rel="noopener">{{ r.filename }}</a>
            </td>
            <td>{{ fmtSize(r.size) }}</td>
            <td>{{ fmtTime(r.mod_time) }}</td>
            <td class="ops">
              <button class="btn ghost danger" @click="remove(r)">删除</button>
            </td>
          </tr>
        </tbody>
      </table>
      <p v-else class="text-dim empty">暂无安装包，请先上传。</p>
    </div>
  </div>
</template>

<style scoped>
.intro { padding: 14px 16px; margin-bottom: 14px; line-height: 1.65; color: var(--text-dim); }
.intro code { font-size: 12px; padding: 1px 6px; border-radius: 4px; background: var(--bg-2, rgba(0,0,0,.06)); }
.panel { padding: 16px; margin-bottom: 14px; }
.sec-head { display: flex; align-items: center; gap: 6px; font-size: 14px; margin: 0 0 12px; }
.cur-row { display: flex; flex-wrap: wrap; gap: 16px 28px; margin-bottom: 12px; }
.kv { display: flex; flex-direction: column; gap: 4px; min-width: 120px; }
.kv.grow { flex: 1; min-width: 220px; }
.k { font-size: 12px; color: var(--text-dim); }
.v { font-size: 15px; word-break: break-all; }
.link { color: var(--primary-2); text-decoration: none; }
.link:hover { text-decoration: underline; }
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; font-size: 13px; }
.field textarea {
  resize: vertical; min-height: 56px; padding: 8px 10px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg, transparent); color: inherit; font: inherit;
}
.force { display: flex; align-items: center; gap: 8px; font-size: 13px; margin-bottom: 12px; cursor: pointer; }
.actions { display: flex; gap: 10px; }
.btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 0; border-radius: 8px; padding: 8px 16px; font-size: 13px; cursor: pointer;
  background: var(--bg-2, #2a2e52); color: inherit;
}
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn.primary { background: linear-gradient(135deg, var(--primary), var(--primary-2, var(--primary))); color: #fff; }
.btn.ghost { background: transparent; border: 1px solid var(--border); }
.btn.danger { color: #ff7b8a; }
.pick { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.picked { font-size: 13px; }
.bar { height: 6px; border-radius: 99px; background: var(--border); overflow: hidden; margin-bottom: 12px; }
.bar i { display: block; height: 100%; background: var(--primary); transition: width .15s; }
.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th, .tbl td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--border); }
.tbl th { color: var(--text-dim); font-weight: 500; }
.tbl tr.cur td { background: color-mix(in srgb, var(--primary) 8%, transparent); }
.fn a { color: inherit; word-break: break-all; }
.badge {
  display: inline-block; margin-left: 6px; font-size: 11px; padding: 1px 6px; border-radius: 99px;
  background: color-mix(in srgb, var(--primary) 22%, transparent); color: var(--primary-2, var(--primary));
}
.ops { white-space: nowrap; }
.empty { padding: 8px 0; }
</style>
