<template>
  <el-dialog
    v-model="visible"
    title="附件预览"
    width="70%"
    top="5vh"
    :close-on-click-modal="true"
    destroy-on-close
  >
    <div v-loading="loading" class="preview-container">
      <!-- 图片 -->
      <div v-if="previewType === 'image'" class="image-preview">
        <el-image :src="fileUrl" fit="contain" style="width: 100%; max-height: 70vh;" />
      </div>

      <!-- PDF -->
      <div v-else-if="previewType === 'pdf'" class="pdf-preview">
        <iframe :src="fileUrl" frameborder="0" width="100%" style="height: 70vh;" />
      </div>

      <!-- Excel -->
      <div v-else-if="previewType === 'excel'" class="excel-preview">
        <el-tabs v-model="activeSheet" type="border-card">
          <el-tab-pane v-for="(sheet, name) in sheets" :key="name" :label="name" :name="name">
            <el-table :data="sheet" size="small" border max-height="60vh">
              <el-table-column
                v-for="col in Object.keys(sheet[0] || {})"
                :key="col"
                :prop="col"
                :label="col"
                min-width="120"
              />
            </el-table>
          </el-tab-pane>
        </el-tabs>
      </div>

      <!-- Word -->
      <div v-else-if="previewType === 'word'" class="word-preview" v-html="purifiedWordHtml" />

      <!-- 不支持的类型 -->
      <div v-else class="unsupported-preview">
        <el-empty description="该文件类型暂不支持在线预览，请下载后查看">
          <el-button type="primary" @click="downloadFile">下载文件</el-button>
        </el-empty>
      </div>
    </div>
  </el-dialog>
</template>

<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { getFileUrl, getFileName } from '@/utils/file'
import DOMPurify from 'dompurify'
import * as XLSX from 'xlsx'
import * as mammoth from 'mammoth'

const props = defineProps<{
  url: string
  modelValue: boolean
}>()

const emit = defineEmits<{
  (e: 'update:modelValue', value: boolean): void
}>()

const visible = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
})

const fileUrl = computed(() => getFileUrl(props.url))
const fileName = computed(() => getFileName(props.url))
const loading = ref(false)
const sheets = ref<Record<string, any[]>>({})
const activeSheet = ref('')
const wordHtml = ref('')
const purifiedWordHtml = computed(() => DOMPurify.sanitize(wordHtml.value))

const previewType = computed(() => {
  const ext = fileName.value.split('.').pop()?.toLowerCase()
  if (!ext) return 'unsupported'
  if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext)) return 'image'
  if (ext === 'pdf') return 'pdf'
  if (['xls', 'xlsx', 'csv'].includes(ext)) return 'excel'
  if (['doc', 'docx'].includes(ext)) return 'word'
  return 'unsupported'
})

watch(() => props.modelValue, async (val) => {
  if (!val) {
    sheets.value = {}
    wordHtml.value = ''
    return
  }
  if (!fileUrl.value) {
    ElMessage.error('文件地址为空，无法预览')
    emit('update:modelValue', false)
    return
  }
  if (previewType.value === 'excel') {
    await loadExcel()
  } else if (previewType.value === 'word') {
    await loadWord()
  }
})

async function loadExcel() {
  loading.value = true
  try {
    const res = await fetch(fileUrl.value)
    const blob = await res.blob()
    const data = await blob.arrayBuffer()
    const workbook = XLSX.read(data, { type: 'array' })
    const result: Record<string, any[]> = {}
    workbook.SheetNames.forEach(name => {
      const worksheet = workbook.Sheets[name]
      result[name] = XLSX.utils.sheet_to_json(worksheet, { header: 1 })
    })
    // 把第一行作为表头
    const formatted: Record<string, any[]> = {}
    Object.entries(result).forEach(([name, rows]) => {
      if (rows.length === 0) {
        formatted[name] = []
        return
      }
      const headers = rows[0] as string[]
      formatted[name] = rows.slice(1).map((row: any) => {
        const obj: Record<string, any> = {}
        headers.forEach((h, i) => {
          obj[h] = row[i] ?? ''
        })
        return obj
      })
    })
    sheets.value = formatted
    activeSheet.value = workbook.SheetNames[0] || ''
  } catch (e) {
    ElMessage.error('Excel 预览失败')
  } finally {
    loading.value = false
  }
}

async function loadWord() {
  loading.value = true
  try {
    const res = await fetch(fileUrl.value)
    const blob = await res.blob()
    const arrayBuffer = await blob.arrayBuffer()
    const result = await mammoth.convertToHtml({ arrayBuffer })
    wordHtml.value = result.value
  } catch (e) {
    ElMessage.error('Word 预览失败')
  } finally {
    loading.value = false
  }
}

function downloadFile() {
  const link = document.createElement('a')
  link.href = fileUrl.value
  link.download = fileName.value
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
}
</script>

<style scoped>
.preview-container {
  min-height: 200px;
}
.image-preview,
.pdf-preview {
  text-align: center;
}
.word-preview {
  max-height: 70vh;
  overflow: auto;
  padding: 16px;
  background: #fff;
  border: 1px solid #ebeef5;
}
.unsupported-preview {
  padding: 40px 0;
}
</style>
