<!-- filePreview -->
<template>
  <el-dialog
    v-model="dialogVisible"
    :title="fileTitle"
    width="90%"
    :close-on-click-modal="false"
    destroy-on-close
    @close="handleClose"
  >
    <div class="preview-container">
      <!-- 图片预览（增加 loading 和错误处理） -->
      <div
        v-if="fileType === 'image'"
        class="preview-image-wrapper"
        v-loading="imageLoading"
        element-loading-text="图片加载中..."
      >
        <div v-if="imageError" class="image-error">
          <el-icon :size="48"><Picture /></el-icon>
          <p>图片加载失败</p>
          <el-button size="small" type="primary" @click="retryImage">重试</el-button>
        </div>
        <img
          v-else
          :src="fileUrl"
          :key="imageKey"
          class="preview-image"
          alt="预览图片"
          @load="onImageLoad"
          @error="onImageError"
        />
      </div>

      <!-- PDF 预览（滚动多页） -->
      <div v-else-if="fileType === 'pdf'" class="pdf-viewer">
        <div class="pdf-toolbar">
          <el-button-group>
            <el-button size="small" @click="zoomOut">
              <el-icon><ZoomOut /></el-icon> 缩小
            </el-button>
            <el-button size="small" @click="zoomIn">
              <el-icon><ZoomIn /></el-icon> 放大
            </el-button>
          </el-button-group>
          <span class="page-info">共 {{ pdfTotalPages }} 页</span>
        </div>
        <div
          class="pdf-scroll-wrapper"
          v-loading="pdfLoading"
          element-loading-text="正在渲染页面..."
        >
          <div ref="pdfScrollContainer" class="pdf-scroll-container"></div>
        </div>
      </div>

      <!-- 不支持预览的文件类型 -->
      <div v-else class="preview-unsupported">
        <el-icon :size="48" color="#909399"><Document /></el-icon>
        <p>暂不支持预览此类型文件</p>
        <el-button type="primary" @click="dialogVisible = false">关闭</el-button>
      </div>
    </div>
  </el-dialog>
</template>

<script setup>
import { ref, computed, watch, nextTick, onBeforeUnmount, shallowRef } from 'vue'
import { ElMessage } from 'element-plus'
import { ZoomIn, ZoomOut, Document, Picture } from '@element-plus/icons-vue'
import * as PDFJS from 'pdfjs-dist'

// 配置 PDF.js worker
PDFJS.GlobalWorkerOptions.workerSrc = '/js/pdf.worker.min.mjs' //在public/js/pdf.worker.min.mjs

// ---------- 内部状态 ----------
const dialogVisible = ref(false) // 弹窗显示状态
const fileUrl = ref('') // 当前预览的文件 URL
const fileName = ref('') // 当前预览的文件名

// 图片预览相关状态
const imageLoading = ref(false) // 图片加载中
const imageError = ref(false) // 图片加载失败
const imageKey = ref(0) // 强制重新加载图片的 key

// PDF 相关
const pdfDoc = shallowRef(null) // pdf 文档实例
const pdfScrollContainer = ref(null) // 滚动容器
const pdfTotalPages = ref(0) // 总页数
const pdfScale = ref(1.2) // 缩放比例
const pdfLoading = ref(false) // 加载状态
let pdfLoadingCanceled = false // 取消标志
let isRendering = false // 防止重复渲染

// ---------- 辅助函数 ----------
// 根据文件名获取文件类型（image / pdf / unsupported）
const getFileType = name => {
  if (!name) return 'unsupported'
  const ext = name.split('.').pop().toLowerCase()
  if (['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'svg'].includes(ext)) {
    return 'image'
  } else if (ext === 'pdf') {
    return 'pdf'
  }
  return 'unsupported'
}

const fileType = computed(() => getFileType(fileName.value))
const fileTitle = computed(() => fileName.value || '文件预览')

// ---------- 图片预览事件处理 ----------
const onImageLoad = () => {
  imageLoading.value = false
  imageError.value = false
}

const onImageError = () => {
  imageLoading.value = false
  imageError.value = true
  ElMessage.error('图片加载失败，请检查文件链接')
}

const retryImage = () => {
  imageError.value = false
  imageLoading.value = true
  imageKey.value++ // 改变 key 强制重新加载图片
}

// ---------- 清理 PDF 资源 ----------
const clearPdf = async () => {
  pdfLoadingCanceled = true // 取消任何进行中的渲染
  pdfLoading.value = false
  isRendering = false
  if (pdfDoc.value) {
    try {
      await pdfDoc.value.destroy()
    } catch (e) {
      // 忽略销毁错误
    }
    pdfDoc.value = null
  }
  pdfTotalPages.value = 0
  pdfScale.value = 1.2
  if (pdfScrollContainer.value) {
    pdfScrollContainer.value.innerHTML = ''
  }
}

// ---------- 渲染 PDF 所有页面（滚动多页）----------
const renderAllPages = async scale => {
  if (!pdfDoc.value || pdfLoadingCanceled || isRendering) return
  isRendering = true
  pdfLoading.value = true

  const container = pdfScrollContainer.value
  if (!container) {
    isRendering = false
    pdfLoading.value = false
    return
  }

  // 清空之前的 canvas
  container.innerHTML = ''

  try {
    const promises = []
    for (let pageNum = 1; pageNum <= pdfTotalPages.value; pageNum++) {
      if (pdfLoadingCanceled) break
      const page = await pdfDoc.value.getPage(pageNum)
      const viewport = page.getViewport({ scale })

      // 创建 canvas 元素
      const canvas = document.createElement('canvas')
      canvas.className = 'pdf-page-canvas'
      const context = canvas.getContext('2d')
      canvas.height = viewport.height
      canvas.width = viewport.width
      canvas.style.marginBottom = '16px'
      canvas.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)'
      container.appendChild(canvas)

      // 渲染当前页
      const renderTask = page.render({
        canvasContext: context,
        viewport: viewport
      })
      promises.push(renderTask.promise)
    }
    await Promise.all(promises)
  } catch (err) {
    if (!pdfLoadingCanceled) {
      console.error('渲染 PDF 失败', err)
      ElMessage.error('渲染 PDF 页面失败')
    }
  } finally {
    isRendering = false
    pdfLoading.value = false
  }
}

// 加载 PDF 文档
const loadPdf = async url => {
  pdfLoadingCanceled = false
  pdfLoading.value = true

  try {
    // 清理旧文档
    if (pdfDoc.value) {
      await pdfDoc.value.destroy().catch(() => {})
      pdfDoc.value = null
    }
    if (pdfScrollContainer.value) {
      pdfScrollContainer.value.innerHTML = ''
    }

    const loadingTask = PDFJS.getDocument(url)
    pdfDoc.value = await loadingTask.promise

    if (pdfLoadingCanceled) {
      if (pdfDoc.value) pdfDoc.value.destroy()
      pdfLoading.value = false
      return
    }

    pdfTotalPages.value = pdfDoc.value.numPages
    await renderAllPages(pdfScale.value)
    pdfLoading.value = false
  } catch (err) {
    if (!pdfLoadingCanceled) {
      console.error('PDF 加载失败', err)
      ElMessage.error('PDF 文件加载失败，请检查文件链接')
      dialogVisible.value = false
    }
    pdfLoading.value = false
  }
}

// ---------- 缩放控制 ----------
const zoomIn = () => {
  if (fileType.value !== 'pdf') return
  pdfScale.value = Math.min(pdfScale.value + 0.2, 3.0)
  renderAllPages(pdfScale.value)
}

const zoomOut = () => {
  if (fileType.value !== 'pdf') return
  pdfScale.value = Math.max(pdfScale.value - 0.2, 0.5)
  renderAllPages(pdfScale.value)
}

// ---------- 关闭弹窗 ----------
const handleClose = async () => {
  // 重置图片相关状态
  imageLoading.value = false
  imageError.value = false
  await clearPdf()
  dialogVisible.value = false
}

// ---------- 对外暴露的方法 ----------
/**
 * 打开预览弹窗
 * @param {Object} file - 文件对象，需包含 url 和 name 属性（或 originalName）
 * @example
 * open({ url: 'https://example.com/file.pdf', name: '文档.pdf' })
 */
const open = file => {
  if (!file || !file.url) {
    ElMessage.warning('无法预览：文件地址不存在')
    return
  }

  // 重置状态
  fileUrl.value = file.url
  fileName.value = file.name || file.originalName || '文件'

  // 根据文件类型重置对应的加载状态
  const type = getFileType(fileName.value)
  if (type === 'image') {
    // 重置图片状态并强制重新加载
    imageLoading.value = true
    imageError.value = false
    imageKey.value++ // 改变 key 确保重新加载图片
  } else if (type === 'pdf') {
    // PDF 相关状态重置已在 loadPdf 中处理
    pdfLoadingCanceled = false
  }

  dialogVisible.value = true

  // 根据文件类型处理
  nextTick(() => {
    if (fileType.value === 'pdf') {
      loadPdf(fileUrl.value)
    }
    // 图片无需额外处理，通过 imageKey 变化自动重新加载
  })
}

// 组件卸载时清理 PDF 资源
onBeforeUnmount(() => {
  if (pdfDoc.value) {
    pdfDoc.value.destroy().catch(() => {})
    pdfDoc.value = null
  }
  pdfLoadingCanceled = true
})

defineExpose({ open })
</script>

<style lang="scss" scoped>
.preview-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 400px;
}

.preview-image-wrapper {
  width: 100%;
  position: relative;
  min-height: 200px;
  text-align: center;
}

.preview-image {
  max-width: 100%;
  max-height: 70vh;
  object-fit: contain;
}

.image-error {
  text-align: center;
  padding: 40px;
  color: #909399;

  p {
    margin: 16px 0;
  }
}

.preview-unsupported {
  text-align: center;
  padding: 40px;

  p {
    margin: 16px 0;
    color: #909399;
  }
}

.pdf-viewer {
  display: flex;
  flex-direction: column;
  height: 70vh;
}

.pdf-toolbar {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 16px;
  padding: 8px 0;
  border-bottom: 1px solid #e4e7ed;
  margin-bottom: 12px;

  .page-info {
    font-size: 14px;
    color: #606266;
  }
}

.pdf-scroll-wrapper {
  width: 100%;
  flex: 1;
  overflow-y: auto;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  background: #f9fafc;
}

.pdf-scroll-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 16px;
}

.pdf-page-canvas {
  display: block;
  max-width: 100%;
  height: auto;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  margin-bottom: 16px;
}
</style>
