<template>
  <div>
    <!-- 品牌区（上传成功后由结果卡片内品牌块替代） -->
    <div v-if="!result" class="text-center pt-1 pb-4 select-none">
      <div
        class="w-11 h-11 mx-auto rounded-xl bg-gradient-to-br from-[#2D6CFF] to-[#00B4FF] grid place-items-center text-white text-lg font-bold shadow-lift"
      >
        传
      </div>
      <h1 class="mt-1.5 text-[22px] font-semibold text-gradient">文传易</h1>
      <p class="mt-0.5 text-[13px] text-[#646A73]">免登录，传文件，真容易</p>
      <div class="mt-2.5 flex items-center justify-center gap-2 text-xs text-[#646A73]">
        <span class="px-2.5 py-1 rounded-full bg-white shadow-card font-medium">1 上传文件</span>
        <span class="text-[#C0C6CF]">→</span>
        <span class="px-2.5 py-1 rounded-full bg-white shadow-card font-medium">2 获得取件码</span>
        <span class="text-[#C0C6CF]">→</span>
        <span class="px-2.5 py-1 rounded-full bg-white shadow-card font-medium">3 发给对方取件</span>
      </div>
    </div>

    <!-- 微信内置浏览器提示 -->
    <div
      v-if="isWeChat && showWeChatTip"
      class="mb-4 px-4 py-3 flex items-start gap-2 text-[13px] rounded-2xl border-2 border-[#FFD591] bg-[#FFF7E8]"
    >
      <div class="flex-1 text-[#8A6213] leading-relaxed">
        微信内上传与下载会受限（无法从聊天选择文件、下载需跳转外部浏览器）。建议点击右上角「···」→「在浏览器打开」。
        <button class="text-[#2D6CFF] font-medium underline underline-offset-2" @click="copySiteLink">复制链接</button>
      </div>
      <button class="text-[#C0A06B] shrink-0" @click="showWeChatTip = false">
        <t-icon name="close" :size="16" />
      </button>
    </div>

    <!-- 上传大卡片 -->
    <div
      v-if="!result"
      class="wcy-card p-5 md:p-6 transition-all duration-300"
      :class="isDragOver ? '!border-[#2D6CFF] bg-[#F0F6FF] scale-[1.01]' : ''"
      style="border: 2px dashed #d6e0f2"
      @dragover.prevent="isDragOver = true"
      @dragleave.prevent="isDragOver = false"
      @drop.prevent="onDrop"
    >
      <!-- 模式切换 -->
      <div class="grid grid-cols-2 gap-1 p-1 rounded-xl bg-[#F0F3F8] mb-4">
        <button
          class="py-2 rounded-lg text-sm font-medium transition-all"
          :class="!textMode ? 'bg-white text-[#2D6CFF] shadow-sm' : 'text-[#646A73]'"
          @click="textMode = false"
        >
          上传文件
        </button>
        <button
          class="py-2 rounded-lg text-sm font-medium transition-all"
          :class="textMode ? 'bg-white text-[#2D6CFF] shadow-sm' : 'text-[#646A73]'"
          @click="textMode = true"
        >
          粘贴文字
        </button>
      </div>

      <!-- 文件模式 -->
      <div v-if="!textMode">
        <div
          v-if="!selectedFile"
          class="relative py-7 flex flex-col items-center justify-center gap-2.5 cursor-pointer text-center"
        >
          <!-- 原生 file input 透明铺满上传区：不显示实体按钮，用户点击上传区时直接点到 input，由系统弹出选择器 -->
          <input
            ref="fileInput"
            type="file"
            class="absolute inset-0 z-10 w-full h-full opacity-0 cursor-pointer"
            @change="onFileChange"
          />
          <div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-[#2D6CFF] to-[#00B4FF]/80 grid place-items-center text-white shadow-card">
            <t-icon name="cloud-upload" :size="26" />
          </div>
          <div>
            <div class="font-medium text-[#1F2329]">点击选择文件 / 拖拽至此 / Ctrl+V 粘贴</div>
            <div class="text-xs text-[#8A9099] mt-1">单个文件不超过 200MB · 微信内可从聊天记录选择</div>
          </div>
        </div>
        <div
          v-else
          class="flex items-center gap-3 p-4 rounded-xl bg-[#F4F7FC]"
        >
          <div class="w-11 h-11 rounded-xl bg-[#EAF1FF] grid place-items-center text-[#2D6CFF] shrink-0">
            <t-icon name="file" :size="22" />
          </div>
          <div class="flex-1 min-w-0">
            <div class="text-sm font-medium truncate">{{ selectedFile.name }}</div>
            <div class="text-xs text-[#8A9099] mt-0.5">{{ formatSize(selectedFile.size) }}</div>
          </div>
          <t-button theme="default" variant="text" @click="selectedFile = null">
            <t-icon name="close" />
          </t-button>
        </div>
      </div>

      <!-- 文字模式 -->
      <div v-else>
        <t-textarea
          v-model="pastedText"
          placeholder="在此粘贴要发送的文字（支持多行换行），自动生成 txt 文件发送"
          :autosize="{ minRows: 4, maxRows: 8 }"
          maxlength="5000"
        />
        <div v-if="pastedText.trim()" class="mt-2 text-xs text-[#8A9099]">
          将保存为 <span class="code-font text-[#2D6CFF]">{{ buildTextFileName(pastedText) }}</span>
        </div>
      </div>

      <!-- 有效期（标签模式下隐藏） -->
      <template v-if="!tag">
        <div class="mt-5 text-sm font-medium text-[#1F2329]">有效期</div>
        <div class="mt-2 grid grid-cols-3 gap-2">
          <button
            v-for="opt in expires"
            :key="opt.value"
            class="py-2.5 rounded-xl text-sm font-medium transition-all"
            :class="expireHours === opt.value
              ? 'bg-gradient-to-br from-[#2D6CFF] to-[#00B4FF] text-white shadow-card'
              : 'bg-[#F0F3F8] text-[#646A73] hover:bg-[#E6ECF5]'"
            @click="expireHours = opt.value"
          >
            {{ opt.label }}
          </button>
        </div>
      </template>
      <div v-else class="mt-5 flex items-center gap-2 text-sm text-[#00B578]">
        <t-icon name="check-circle" /> 标签文件永久保存，有效期固定为「永久」
      </div>

      <!-- 密码与标签：两个独立输入区，可同时设置 -->
      <div class="mt-5 space-y-3">
        <div class="rounded-2xl border-2 border-[#2D6CFF]/30 bg-[#F0F6FF] p-4">
          <div class="flex items-center gap-1.5 text-[15px] font-semibold text-[#1F2329]">
            <span class="w-6 h-6 rounded-lg bg-[#2D6CFF]/15 grid place-items-center text-[#2D6CFF]"><t-icon name="lock-on" :size="14" /></span>
            设置下载密码
            <span class="ml-auto text-xs text-[#8A9099] font-normal">选填 · 4-12 位</span>
          </div>
          <t-input
            v-model="password"
            type="password"
            placeholder="设置后对方需输入密码才能取件"
            maxlength="12"
            clearable
            class="mt-3 !bg-white !rounded-xl shadow-sm"
            @update:model-value="validatePassword"
          />
          <div v-if="passwordError" class="mt-1.5 text-xs text-[#FF4D4F]">{{ passwordError }}</div>
        </div>
        <div class="rounded-2xl border-2 border-[#00B4FF]/30 bg-[#F0FAFF] p-4">
          <div class="flex items-center gap-1.5 text-[15px] font-semibold text-[#1F2329]">
            <span class="w-6 h-6 rounded-lg bg-[#00B4FF]/15 grid place-items-center text-[#00B4FF]"><t-icon name="label" :size="14" /></span>
            设置文件标签
            <span class="ml-auto text-xs text-[#8A9099] font-normal">选填</span>
          </div>
          <t-input
            v-model="tag"
            placeholder="仅限输入英文或者数字，数字需超过8位，纯英文必须超过4位数"
            maxlength="32"
            clearable
            class="mt-3 !bg-white !rounded-xl shadow-sm"
            @update:model-value="onTagInput"
          />
          <div v-if="tagError" class="mt-1.5 text-xs text-[#FF4D4F]">{{ tagError }}</div>
          <div v-else-if="tag && /^[0-9]+$/.test(tag)" class="mt-1.5 text-xs text-[#FF8800]">纯数字标签建议使用 11 位手机号码</div>
          <div v-else-if="tag" class="mt-1.5 text-xs text-[#00B578]">标签可重复使用，关联多个文件</div>
        </div>
      </div>

      <!-- 上传按钮 + 进度 -->
      <div class="mt-5">
        <button
          class="w-full py-3.5 rounded-xl text-white font-semibold text-[15px] transition-all bg-gradient-to-br from-[#2D6CFF] to-[#00B4FF] shadow-card hover:shadow-lift hover:-translate-y-0.5 disabled:opacity-60 disabled:hover:translate-y-0"
          :disabled="uploading || !canUpload"
          @click="submit"
        >
          {{ uploading ? `上传中 ${progress}%` : '上传文件' }}
        </button>
        <div v-if="uploading" class="mt-2 h-1.5 rounded-full bg-[#EAF1FF] overflow-hidden">
          <div class="h-full rounded-full bg-gradient-to-r from-[#2D6CFF] to-[#00B4FF] transition-all" :style="{ width: progress + '%' }" />
        </div>
      </div>
    </div>

    <!-- 上传成功结果卡片 -->
    <div v-else class="wcy-card p-6 text-center fade-enter-active" style="animation: fadeIn 0.3s ease">
      <!-- 品牌块（截图时品牌可见） -->
      <div class="flex items-center justify-center gap-2.5">
        <div class="w-9 h-9 rounded-xl bg-gradient-to-br from-[#2D6CFF] to-[#00B4FF] grid place-items-center text-white text-lg font-bold shadow-card">
          传
        </div>
        <div class="text-left">
          <div class="text-xl font-bold text-gradient leading-none">文传易</div>
          <div class="text-xs text-[#646A73] mt-1.5 leading-none">免登录，传文件，真容易</div>
        </div>
      </div>

      <div class="mt-4 text-[15px] font-semibold text-[#00B578] flex items-center justify-center gap-1">
        <t-icon name="check-circle" :size="18" /> 上传成功，凭证已发出
      </div>

      <!-- 下载提示：手机端 / 电脑端分行排版 -->
      <div class="mt-4 rounded-2xl bg-[#F4F7FC] p-4 text-left">
        <div class="flex items-start gap-2.5">
          <span class="mt-0.5 w-7 h-7 rounded-lg bg-[#EAF1FF] text-[#2D6CFF] grid place-items-center shrink-0">
            <t-icon name="mobile" :size="15" />
          </span>
          <div class="min-w-0">
            <div class="text-[13px] font-medium text-[#1F2329]">手机端</div>
            <div class="text-xs text-[#646A73] mt-0.5 leading-relaxed">直接识别下方二维码下载文件</div>
          </div>
        </div>
        <div class="flex items-start gap-2.5 mt-3">
          <span class="mt-0.5 w-7 h-7 rounded-lg bg-[#EAF1FF] text-[#2D6CFF] grid place-items-center shrink-0">
            <t-icon name="desktop" :size="15" />
          </span>
          <div class="min-w-0">
            <div class="text-[13px] font-medium text-[#1F2329]">电脑端</div>
            <div class="text-xs text-[#646A73] mt-0.5 leading-relaxed">
              登录文传易网站
              <span class="text-[#2D6CFF] font-medium break-all">{{ siteOrigin }}</span>
              输入取件码下载
            </div>
          </div>
        </div>
      </div>

      <!-- 文件信息：文件名 / 大小 / 有效期 -->
      <div class="mt-3 rounded-2xl bg-[#F4F7FC] p-3.5 text-left text-[13px]">
        <div class="flex gap-2">
          <span class="text-[#8A9099] shrink-0 w-16">文件名</span>
          <span class="text-[#1F2329] break-all min-w-0 flex-1">{{ result.originalName }}</span>
        </div>
        <div class="flex gap-2 mt-1.5">
          <span class="text-[#8A9099] shrink-0 w-16">大小</span>
          <span class="text-[#1F2329]">{{ formatSize(result.size) }}</span>
        </div>
        <div class="flex gap-2 mt-1.5">
          <span class="text-[#8A9099] shrink-0 w-16">有效期</span>
          <span class="text-[#1F2329]">{{ result.isPermanent ? '永久有效' : `至 ${formatDateTime(result.expiresAt)}` }}</span>
        </div>
      </div>

      <!-- 二维码（直接展示；保存按钮点击后才生成区域图片，界面不预嵌入图片） -->
      <div class="mt-5">
        <img
          v-if="qrDataUrl"
          :src="qrDataUrl"
          alt="取件二维码"
          class="w-52 h-52 md:w-56 md:h-56 mx-auto rounded-2xl border border-[#F0F3F8]"
        />
      </div>

      <div class="code-font text-[30px] font-bold text-[#1F2329] mt-4 tracking-[0.1em]">{{ formatPickCode(result.pickCode) }}</div>

      <div class="flex gap-3 justify-center mt-5">
        <button
          class="px-5 py-2.5 rounded-xl text-white text-sm font-medium bg-gradient-to-br from-[#2D6CFF] to-[#00B4FF] shadow-card hover:shadow-lift transition-all"
          @click="copyPickCode"
        >
          复制取件凭证
        </button>
        <button
          class="px-5 py-2.5 rounded-xl text-[#2D6CFF] text-sm font-medium bg-[#EAF1FF] hover:bg-[#DFEAFF] transition-all"
          @click="downloadQr"
        >
          保存二维码
        </button>
      </div>

      <!-- 管理码（可收起） -->
      <div class="mt-5 border-t border-[#F0F3F8] pt-3 text-left">
        <button class="w-full flex items-center justify-between text-sm text-[#646A73]" @click="showAdmin = !showAdmin">
          <span>管理码（凭此删除文件）</span>
          <t-icon :name="showAdmin ? 'chevron-up' : 'chevron-down'" />
        </button>
        <div v-show="showAdmin" class="mt-2 flex items-center gap-2">
          <div class="flex-1 code-font text-lg bg-[#F4F7FC] rounded-xl px-3 py-2 text-[#1F2329]">{{ result.adminCode }}</div>
          <button class="px-3 py-2 rounded-lg text-sm text-[#2D6CFF] bg-[#EAF1FF]" @click="copyText(result.adminCode)">复制</button>
        </div>
        <div class="mt-2 text-xs text-[#8A9099]">管理码仅展示一次，不提供找回，请妥善保存</div>
      </div>

      <button class="mt-5 text-sm text-[#2D6CFF] underline underline-offset-4" @click="reset">
        再传一个
      </button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { MessagePlugin } from 'tdesign-vue-next'
import QRCode from 'qrcode'
import { uploadFile, type UploadResult } from '@/api'

const expires = [
  { label: '24 小时', value: 24 },
  { label: '7 天', value: 168 },
  { label: '永久', value: 0 },
]

const textMode = ref(false)
const fileInput = ref<HTMLInputElement | null>(null)
const isWeChat = ref(false)
const showWeChatTip = ref(true)

// 微信内置浏览器上传/下载受限，提供复制链接引导用户在外部浏览器打开
function copySiteLink() {
  const url = location.href
  const ok = () => MessagePlugin.success('链接已复制，请到浏览器中粘贴打开')
  if (navigator.clipboard?.writeText) {
    navigator.clipboard.writeText(url).then(ok).catch(() => fallbackCopy(url))
  } else {
    fallbackCopy(url)
  }
}
function fallbackCopy(text: string) {
  const ta = document.createElement('textarea')
  ta.value = text
  ta.style.position = 'fixed'
  ta.style.opacity = '0'
  ta.style.left = '-9999px'
  document.body.appendChild(ta)
  ta.select()
  try {
    document.execCommand('copy')
    MessagePlugin.success('链接已复制，请到浏览器中粘贴打开')
  } catch {
    MessagePlugin.error('复制失败，请长按地址栏手动复制')
  }
  document.body.removeChild(ta)
}
const selectedFile = ref<File | null>(null)
const pastedText = ref('')
const expireHours = ref(24)
const password = ref('')
const passwordError = ref('')
const tag = ref('')
const tagError = ref('')
const isDragOver = ref(false)
const uploading = ref(false)
const progress = ref(0)
const result = ref<UploadResult | null>(null)
const posterDataUrl = ref('')
const qrDataUrl = ref('')
const showAdmin = ref(false)

// 站点地址（与取件二维码保持一致，随部署环境自动变化）
const siteOrigin = computed(() => {
  if (!result.value) return ''
  try {
    return new URL(result.value.pickUrl).origin
  } catch {
    return ''
  }
})

// 8 位取件码按 4+4 格式化展示（6 位码保持原样）
function formatPickCode(code: string) {
  return code.length === 8 ? `${code.slice(0, 4)} ${code.slice(4)}` : code
}

const canUpload = computed(() => {
  if (textMode.value) return !!pastedText.value.trim() && !passwordError.value && !tagError.value
  return !!selectedFile.value && !passwordError.value && !tagError.value
})

function onFileChange(e: Event) {
  const input = e.target as HTMLInputElement
  if (input.files?.length) handleFile(input.files[0])
  input.value = ''
}

function onDrop(e: DragEvent) {
  isDragOver.value = false
  if (e.dataTransfer?.files.length) handleFile(e.dataTransfer.files[0])
}

function handleFile(f: File) {
  if (f.size > 200 * 1024 * 1024) {
    MessagePlugin.error('文件超过 200MB 限制，无法上传')
    return
  }
  const cleaned = sanitizeFileName(f.name)
  if (cleaned !== f.name) {
    selectedFile.value = new File([f], cleaned, { type: f.type })
    MessagePlugin.warning(`文件名含不合法字符，已自动调整为：${cleaned}`)
  } else {
    selectedFile.value = f
  }
}

// 文件名合法性清洗：过滤 Windows 非法字符 / 控制字符、首尾空格与点、保留设备名、超长截断
function sanitizeFileName(name: string) {
  const dot = name.lastIndexOf('.')
  const base = dot >= 0 ? name.slice(0, dot) : name
  const ext = dot >= 0 ? name.slice(dot) : ''
  const cleanBase = base.replace(/[<>:"/\\|?*\u0000-\u001f\u007f]/g, '').trim().replace(/[. ]+$/g, '')
  let result = cleanBase || '未命名'
  if (/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(result)) result = `_${result}`
  if (Array.from(result).length > 180) result = Array.from(result).slice(0, 180).join('')
  const cleanExt = Array.from(ext.replace(/[^a-zA-Z0-9.]/g, '')).join('').replace(/^\.+/, '')
  return result + (cleanExt ? `.${cleanExt.slice(0, 10)}` : '')
}

function onPaste(e: ClipboardEvent) {
  const files = e.clipboardData?.files
  if (files && files.length) {
    handleFile(files[0])
  }
}

function validatePassword() {
  const v = password.value
  if (v && (v.length < 4 || v.length > 12)) {
    passwordError.value = '密码长度须为 4-12 位'
  } else {
    passwordError.value = ''
  }
}

function onTagInput() {
  const v = tag.value.trim()
  if (!/^[A-Za-z0-9]+$/.test(v)) {
    tagError.value = '标签仅允许英文与数字，不能包含符号或空格'
  } else if (/^[0-9]+$/.test(v)) {
    tagError.value = v.length > 8 ? '' : '纯数字标签须超过 8 位（建议使用 11 位手机号码）'
  } else {
    tagError.value = v.length > 4 ? '' : '含英文字母的标签须超过 4 位'
  }
  if (!v) tagError.value = ''
}

function buildTextFileName(text: string) {
  const illegal = /[<>:"/\\|?*\u0000-\u001f]/g
  const head = Array.from(text.trim().slice(0, 12)).join('').replace(illegal, '')
  const name = head || '文字'
  const now = new Date()
  const pad = (n: number) => String(n).padStart(2, '0')
  return `${name}_${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}.txt`
}

async function submit() {
  if (uploading.value) return
  let file: File
  if (textMode.value) {
    // 注意：Content-Type 不能带 ;charset= 参数，OSS SDK 对其签名会与服务端校验不一致导致上传失败
    file = new File([pastedText.value], buildTextFileName(pastedText.value), { type: 'text/plain' })
  } else {
    if (!selectedFile.value) return
    file = selectedFile.value
  }
  if (passwordError.value || tagError.value) return

  const fd = new FormData()
  fd.append('file', file)
  fd.append('expireHours', String(tag.value.trim() ? 0 : expireHours.value))
  if (password.value) fd.append('password', password.value)
  if (tag.value.trim()) fd.append('tag', tag.value.trim())

  uploading.value = true
  progress.value = 0
  try {
    const res = await uploadFile(fd, (p) => (progress.value = p))
    result.value = res.data
    qrDataUrl.value = await QRCode.toDataURL(res.data.pickUrl, {
      width: 480,
      margin: 1,
      color: { dark: '#1F2329', light: '#FFFFFF' },
    })
    MessagePlugin.success('上传成功')
  } catch (e: any) {
    const msg = e?.response?.data?.message || '上传失败，请稍后重试'
    MessagePlugin.error(msg)
  } finally {
    uploading.value = false
  }
}

function reset() {
  result.value = null
  posterDataUrl.value = ''
  qrDataUrl.value = ''
  selectedFile.value = null
  pastedText.value = ''
  password.value = ''
  tag.value = ''
  expireHours.value = 24
  showAdmin.value = false
}

// ===== 取件凭证海报（二维码 + 使用帮助 + 文件信息 合成图） =====
function loadImage(src: string): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.onload = () => resolve(img)
    img.onerror = reject
    img.src = src
  })
}

function roundRect(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number) {
  ctx.beginPath()
  ctx.moveTo(x + r, y)
  ctx.arcTo(x + w, y, x + w, y + h, r)
  ctx.arcTo(x + w, y + h, x, y + h, r)
  ctx.arcTo(x, y + h, x, y, r)
  ctx.arcTo(x, y, x + w, y, r)
  ctx.closePath()
}

async function buildPoster(d: UploadResult): Promise<string> {
  const FONT = '"Microsoft YaHei", "PingFang SC", "Segoe UI", sans-serif'
  const qr = await QRCode.toDataURL(d.pickUrl, {
    width: 720,
    margin: 1,
    color: { dark: '#1F2329', light: '#FFFFFF' },
  })
  const qrImg = await loadImage(qr)

  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')!
  ctx.textBaseline = 'top'
  ctx.textAlign = 'left'

  // 自动换行（按像素宽度测量）
  const wrap = (text: string, maxW: number): string[] => {
    const lines: string[] = []
    let cur = ''
    for (const ch of Array.from(text)) {
      const t = cur + ch
      if (ctx.measureText(t).width > maxW && cur) {
        lines.push(cur)
        cur = ch
      } else {
        cur = t
      }
    }
    if (cur) lines.push(cur)
    return lines
  }

  const W = 600
  ctx.font = `15px ${FONT}`
  const siteLines = wrap(siteOrigin.value || d.pickUrl, 470)
  const nameLines = wrap(d.originalName, 440)
  const H = 950 + (siteLines.length - 1) * 24 + (nameLines.length - 1) * 24
  canvas.width = W
  canvas.height = H

  // 背景
  ctx.fillStyle = '#FFFFFF'
  ctx.fillRect(0, 0, W, H)

  // 顶部品牌条
  const g = ctx.createLinearGradient(0, 0, W, 0)
  g.addColorStop(0, '#2D6CFF')
  g.addColorStop(1, '#00B4FF')
  ctx.fillStyle = g
  roundRect(ctx, 0, 0, W, 100, 24)
  ctx.fill()
  ctx.fillStyle = 'rgba(255,255,255,0.18)'
  roundRect(ctx, 48, 24, 52, 52, 14)
  ctx.fill()
  ctx.fillStyle = '#FFFFFF'
  ctx.textBaseline = 'middle'
  ctx.textAlign = 'center'
  ctx.font = `bold 26px ${FONT}`
  ctx.fillText('传', 74, 50) // logo 圆块 24-76 范围的中线
  ctx.textAlign = 'left'
  ctx.fillText('文传易', 118, 36) // 与 logo 中线略上对齐（视觉与"免登录"副标题更平衡）
  ctx.font = `14px ${FONT}`
  ctx.fillStyle = 'rgba(255,255,255,0.9)'
  ctx.fillText('免登录，传文件，真容易', 118, 64)
  ctx.textBaseline = 'top' // 恢复默认给后续 sectionTitle 等使用

  // 成功提示
  ctx.fillStyle = '#00B578'
  ctx.textAlign = 'center'
  ctx.font = `600 18px ${FONT}`
  ctx.fillText('✓ 上传成功，凭证已发出', W / 2, 120)

  // 分区小标题
  const sectionTitle = (text: string, y: number) => {
    ctx.fillStyle = '#2D6CFF'
    roundRect(ctx, 38, y + 2, 4, 15, 2)
    ctx.fill()
    ctx.fillStyle = '#1F2329'
    ctx.textAlign = 'left'
    ctx.font = `600 15px ${FONT}`
    ctx.fillText(text, 66, y)
  }

  let y = 164
  sectionTitle('下载方式', y)
  y += 36

  // 设备说明行（图标 + 标题 + 说明文字）
  const deviceRow = (type: 'phone' | 'desktop', title: string, lines: string[]) => {
    const x = 44
    ctx.fillStyle = '#EAF1FF'
    roundRect(ctx, x, y, 30, 30, 9)
    ctx.fill()
    ctx.fillStyle = '#2D6CFF'
    if (type === 'phone') {
      roundRect(ctx, x + 9, y + 5, 12, 20, 3)
      ctx.fill()
      ctx.fillStyle = '#EAF1FF'
      roundRect(ctx, x + 12, y + 17, 6, 4, 1.5)
      ctx.fill()
    } else {
      roundRect(ctx, x + 5, y + 5, 20, 14, 3)
      ctx.fill()
      ctx.fillStyle = '#EAF1FF'
      roundRect(ctx, x + 9, y + 8, 12, 8, 1.5)
      ctx.fill()
      ctx.fillStyle = '#2D6CFF'
      ctx.fillRect(x + 8, y + 19, 14, 2.5)
      ctx.fillRect(x + 4, y + 21.5, 22, 3.5)
    }
    const tx = x + 42
    ctx.fillStyle = '#1F2329'
    ctx.font = `600 15px ${FONT}`
    ctx.fillText(title, tx, y + 1)
    ctx.fillStyle = '#646A73'
    ctx.font = `14px ${FONT}`
    let ly = y + 28
    for (const line of lines) {
      ctx.fillText(line, tx, ly)
      ly += 24
    }
    y = ly + 8
  }

  deviceRow('phone', '手机端', ['直接识别下方二维码下载文件'])
  deviceRow('desktop', '电脑端', ['登录文传易网站', ...siteLines])

  y += 8
  sectionTitle('文件信息', y)
  y += 36

  // 文件信息卡（精确高度：上下各 16 padding + 文件名 N*24 + 大小行 24 + 有效期行 24）
  const cardH = 16 + nameLines.length * 24 + 24 + 24 + 16
  ctx.fillStyle = '#F4F8FF'
  roundRect(ctx, 44, y, W - 88, cardH, 16)
  ctx.fill()
  let cy = y + 18
  ctx.fillStyle = '#8A9099'
  ctx.font = `14px ${FONT}`
  ctx.fillText('文件名', 62, cy)
  ctx.fillStyle = '#1F2329'
  ctx.font = `15px ${FONT}`
  ctx.fillText(nameLines[0], 130, cy)
  for (let i = 1; i < nameLines.length; i++) {
    cy += 24
    ctx.fillText(nameLines[i], 130, cy)
  }
  // 大小（单独一行）
  cy += 28
  ctx.fillStyle = '#8A9099'
  ctx.font = `14px ${FONT}`
  ctx.fillText('大小', 62, cy)
  ctx.fillStyle = '#1F2329'
  ctx.font = `15px ${FONT}`
  ctx.fillText(formatSize(d.size), 130, cy)
  // 有效期（单独一行）
  cy += 24
  ctx.fillStyle = '#8A9099'
  ctx.font = `14px ${FONT}`
  ctx.fillText('有效期', 62, cy)
  ctx.fillStyle = '#1F2329'
  ctx.font = `15px ${FONT}`
  const expireText = d.isPermanent ? '永久有效' : `至 ${formatDateTime(d.expiresAt)}`
  ctx.fillText(expireText, 130, cy)

  // 取件码标签（与大码拉开间距避免重叠）
  let codeY = y + cardH + 28
  ctx.fillStyle = '#8A9099'
  ctx.font = `14px ${FONT}`
  ctx.textAlign = 'center'
  ctx.fillText('取 件 码', W / 2, codeY)

  // 取件码大字（多留一行间距，避免与上方标签重叠）
  codeY += 40
  ctx.fillStyle = '#1F2329'
  ctx.font = `bold 38px ${FONT}`
  ctx.fillText(formatPickCode(d.pickCode), W / 2, codeY)

  // 二维码
  const qrSize = 280
  const qrX = (W - qrSize) / 2
  const qrY = codeY + 56
  ctx.fillStyle = '#FFFFFF'
  ctx.strokeStyle = '#E6EBF5'
  ctx.lineWidth = 1
  roundRect(ctx, qrX - 14, qrY - 14, qrSize + 28, qrSize + 28, 16)
  ctx.fill()
  ctx.stroke()
  ctx.drawImage(qrImg, qrX, qrY, qrSize, qrSize)

  // 动态画布高度（按实际内容，消除底部大留白）
  // 注意：重设 canvas.height 会清空画布内容，必须先备份再画回
  // 关键：drawImage 必须显式指定目标尺寸=backup 尺寸，否则浏览器会按新 canvas.height 缩放整张图，导致文字比例全乱
  const newH = qrY + qrSize + 14 + 40
  if (Math.abs(canvas.height - newH) > 1) {
    const backup = document.createElement('canvas')
    backup.width = canvas.width
    backup.height = canvas.height
    backup.getContext('2d')!.drawImage(canvas, 0, 0)
    canvas.height = newH
    canvas.getContext('2d')!.drawImage(backup, 0, 0, backup.width, backup.height)
  }

  return canvas.toDataURL('image/png')
}

// 复制完整取件凭证文案（提示文字 + 网站地址 + 取件码）
function copyPickCode() {
  const d = result.value
  if (!d) return
  copyText(
    [
      '【文传易】免登录，传文件，真容易',
      `文件名：${d.originalName}`,
      `取件码：${d.pickCode}`,
      `手机端：识别二维码直接下载；电脑端：登录文传易网站 ${siteOrigin.value} 输入取件码下载。`,
    ].join('\n')
  )
}

async function copyText(text: string) {
  try {
    await navigator.clipboard.writeText(text)
  } catch {
    const ta = document.createElement('textarea')
    ta.value = text
    ta.style.position = 'fixed'
    ta.style.opacity = '0'
    document.body.appendChild(ta)
    ta.select()
    document.execCommand('copy')
    ta.remove()
  }
  MessagePlugin.success('已复制')
}

// 点击“保存二维码”时，才把成功卡片区域合成一张图片保存（界面不预嵌入图片）
async function downloadQr() {
  if (!result.value) return
  posterDataUrl.value = await buildPoster(result.value)
  // 保存文件名：文传易取件码-取件码(原文件名)-流水号.png（清洗 Windows 非法字符）
  const safeName = (result.value.originalName || '')
    .replace(/[\\/:*?"<>|\u0000-\u001f]/g, '_')
    .replace(/\s+/g, ' ')
    .trim() || '未命名'
  const a = document.createElement('a')
  a.href = posterDataUrl.value
  a.download = `文传易取件码-${result.value.pickCode}(${safeName})-${result.value.id}.png`
  a.click()
}

function formatSize(size: number) {
  if (size < 1024) return size + ' B'
  if (size < 1024 * 1024) return (size / 1024).toFixed(1) + ' KB'
  if (size < 1024 * 1024 * 1024) return (size / 1024 / 1024).toFixed(1) + ' MB'
  return (size / 1024 / 1024 / 1024).toFixed(2) + ' GB'
}

function formatDateTime(s: string | null) {
  if (!s) return ''
  return s.replace('T', ' ').slice(0, 16)
}

onMounted(() => {
  isWeChat.value = /MicroMessenger/i.test(navigator.userAgent)
  window.addEventListener('paste', onPaste)
})
onUnmounted(() => window.removeEventListener('paste', onPaste))
</script>
