<script setup>
import { onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'

import { clearScreenshots, deleteScreenshot, fetchScreenshots } from '../api/screenshots'

const loading = ref(false)
const screenshots = ref([])

const previewOpen = ref(false)
const previewUrl = ref('')
const previewTitle = ref('')

function buildUrl(filename) {
  return `/screenshots/${encodeURIComponent(filename)}`
}

async function load() {
  loading.value = true
  try {
    const data = await fetchScreenshots()
    screenshots.value = Array.isArray(data) ? data : []
  } catch (e) {
    if (e?.response?.status === 401) window.location.href = '/login'
    screenshots.value = []
  } finally {
    loading.value = false
  }
}

function openPreview(item) {
  previewTitle.value = item.display_name || item.filename || '截图预览'
  previewUrl.value = buildUrl(item.filename)
  previewOpen.value = true
}

function findRenderedShotImage(filename) {
  try {
    const escaped = typeof CSS !== 'undefined' && typeof CSS.escape === 'function' ? CSS.escape(String(filename)) : String(filename)
    return document.querySelector(`img[data-shot-filename="${escaped}"]`)
  } catch {
    return null
  }
}

function canvasToPngBlob(canvas) {
  return new Promise((resolve, reject) => {
    canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new Error('toBlob_failed'))), 'image/png')
  })
}

async function imageElementToPngBlob(imgEl) {
  if (!imgEl) throw new Error('no_image')
  if (!imgEl.complete || imgEl.naturalWidth <= 0) {
    if (typeof imgEl.decode === 'function') await imgEl.decode()
    else {
      await new Promise((resolve, reject) => {
        imgEl.addEventListener('load', resolve, { once: true })
        imgEl.addEventListener('error', reject, { once: true })
      })
    }
  }

  const canvas = document.createElement('canvas')
  canvas.width = imgEl.naturalWidth
  canvas.height = imgEl.naturalHeight
  const ctx = canvas.getContext('2d')
  if (!ctx) throw new Error('no_canvas')
  ctx.drawImage(imgEl, 0, 0)
  return await canvasToPngBlob(canvas)
}

async function blobToPng(blob) {
  if (!blob) throw new Error('no_blob')
  if (blob.type === 'image/png') return blob

  if (typeof createImageBitmap === 'function') {
    const bitmap = await createImageBitmap(blob)
    const canvas = document.createElement('canvas')
    canvas.width = bitmap.width
    canvas.height = bitmap.height
    const ctx = canvas.getContext('2d')
    if (!ctx) throw new Error('no_canvas')
    ctx.drawImage(bitmap, 0, 0)
    return await canvasToPngBlob(canvas)
  }

  const url = URL.createObjectURL(blob)
  try {
    const img = new Image()
    img.src = url
    if (typeof img.decode === 'function') await img.decode()
    return await imageElementToPngBlob(img)
  } finally {
    URL.revokeObjectURL(url)
  }
}

async function screenshotUrlToPngBlob(url, filename) {
  // 优先使用页面上已渲染完成的 <img>（避免额外请求；也更容易满足剪贴板“用户手势”限制）
  const imgEl = findRenderedShotImage(filename)
  if (imgEl) {
    try {
      return await imageElementToPngBlob(imgEl)
    } catch {
      // fallback to fetch
    }
  }

  const resp = await fetch(url, { credentials: 'include', cache: 'no-store' })
  if (!resp.ok) throw new Error('fetch_failed')
  const blob = await resp.blob()
  const mime = resp.headers.get('Content-Type') || blob.type || ''
  if (!mime.startsWith('image/')) throw new Error('not_image')
  return await blobToPng(blob)
}

async function onClearAll() {
  try {
    await ElMessageBox.confirm('确定要清空全部截图吗？', '清空截图', {
      confirmButtonText: '清空',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }

  try {
    const res = await clearScreenshots()
    if (res?.success) {
      ElMessage.success(`已清空（删除 ${res?.deleted || 0} 张）`)
      screenshots.value = []
      previewOpen.value = false
      return
    }
    ElMessage.error(res?.error || '操作失败')
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '操作失败')
  }
}

async function onDelete(item) {
  try {
    await ElMessageBox.confirm(`确定要删除截图「${item.display_name || item.filename}」吗？`, '删除截图', {
      confirmButtonText: '删除',
      cancelButtonText: '取消',
      type: 'warning',
    })
  } catch {
    return
  }

  try {
    const res = await deleteScreenshot(item.filename)
    if (res?.success) {
      screenshots.value = screenshots.value.filter((s) => s.filename !== item.filename)
      if (previewUrl.value.includes(encodeURIComponent(item.filename))) previewOpen.value = false
      ElMessage.success('已删除')
      return
    }
    ElMessage.error(res?.error || '删除失败')
  } catch (e) {
    const data = e?.response?.data
    ElMessage.error(data?.error || '删除失败')
  }
}

async function copyImage(item) {
  const url = buildUrl(item.filename)
  if (
    !navigator.clipboard ||
    typeof navigator.clipboard.write !== 'function' ||
    typeof window.ClipboardItem === 'undefined'
  ) {
    ElMessage.warning('当前环境不支持复制图片（建议使用 Chrome/Edge 并通过 HTTPS 访问）；可用“下载”。')
    return
  }

  try {
    // 关键点：用 Promise 形式的数据源，让 clipboard.write 在用户手势内立即发生（更稳）
    try {
      await navigator.clipboard.write([
        new ClipboardItem({
          'image/png': screenshotUrlToPngBlob(url, item.filename),
        }),
      ])
    } catch {
      const pngBlob = await screenshotUrlToPngBlob(url, item.filename)
      await navigator.clipboard.write([new ClipboardItem({ 'image/png': pngBlob })])
    }
    ElMessage.success('图片已复制到剪贴板')
  } catch (e) {
    try {
      if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
        await navigator.clipboard.writeText(`${window.location.origin}${url}`)
        ElMessage.warning('复制图片失败，已复制图片链接（可直接粘贴到浏览器打开）')
        return
      }
    } catch {
      // ignore
    }
    ElMessage.warning('复制图片失败：请确认允许剪贴板权限；可用“下载”。')
  }
}

function download(item) {
  const link = document.createElement('a')
  link.href = buildUrl(item.filename)
  link.download = item.display_name || item.filename
  document.body.appendChild(link)
  link.click()
  link.remove()
}

onMounted(load)
</script>

<template>
  <el-card shadow="never" class="panel" :body-style="{ padding: '14px' }">
    <div class="panel-head">
      <div class="panel-title">截图管理</div>
      <div class="panel-actions">
        <el-button :loading="loading" @click="load">刷新</el-button>
        <el-button type="danger" plain :disabled="screenshots.length === 0" @click="onClearAll">清空全部</el-button>
      </div>
    </div>

    <el-skeleton v-if="loading" :rows="6" animated />
    <template v-else>
      <el-empty v-if="screenshots.length === 0" description="暂无截图" />

      <div v-else class="grid">
        <el-card v-for="item in screenshots" :key="item.filename" shadow="never" class="shot-card" :body-style="{ padding: '0' }">
          <img
            class="shot-img"
            :src="buildUrl(item.filename)"
            :alt="item.display_name || item.filename"
            :data-shot-filename="item.filename"
            loading="lazy"
            @click="openPreview(item)"
          />
          <div class="shot-body">
            <div class="shot-name" :title="item.display_name || item.filename">{{ item.display_name || item.filename }}</div>
            <div class="shot-meta app-muted">{{ item.created || '' }}</div>
            <div class="shot-actions">
              <el-button size="small" text type="primary" @click="copyImage(item)">复制图片</el-button>
              <el-button size="small" text @click="download(item)">下载</el-button>
              <el-button size="small" text type="danger" @click="onDelete(item)">删除</el-button>
            </div>
          </div>
        </el-card>
      </div>
    </template>

    <el-dialog v-model="previewOpen" :title="previewTitle" width="min(920px, 94vw)">
      <div class="preview">
        <img :src="previewUrl" :alt="previewTitle" class="preview-img" />
      </div>
      <template #footer>
        <el-button @click="previewOpen = false">关闭</el-button>
      </template>
    </el-dialog>
  </el-card>
</template>

<style scoped>
.panel {
  border-radius: var(--app-radius);
  border: 1px solid var(--app-border);
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}

.panel-title {
  font-size: 16px;
  font-weight: 900;
}

.panel-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 12px;
  align-items: start;
}

.shot-card {
  border-radius: 14px;
  border: 1px solid var(--app-border);
  overflow: hidden;
}

.shot-img {
  width: 100%;
  aspect-ratio: 16/9;
  object-fit: cover;
  cursor: pointer;
  display: block;
}

.shot-body {
  padding: 12px;
}

.shot-name {
  font-size: 13px;
  font-weight: 800;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.shot-meta {
  margin-top: 4px;
  font-size: 12px;
}

.shot-actions {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.preview {
  display: flex;
  justify-content: center;
}

.preview-img {
  max-width: 100%;
  max-height: 78vh;
  object-fit: contain;
  border-radius: 10px;
  border: 1px solid var(--app-border);
  background: #fff;
}

@media (max-width: 480px) {
  .grid {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 768px) {
  .panel-actions {
    width: 100%;
    justify-content: flex-end;
  }
}
</style>
