<template>
  <div class="universal-file-reader">
    <div v-if="loading" class="loading-container">
      <a-spin size="large" />
      <p>正在加载文件...</p>
    </div>

    <div v-else-if="error" class="error-container">
      <CloseCircleOutlined style="font-size: 48px; color: #ff4d4f;" />
      <h2>加载失败</h2>
      <p>{{ error }}</p>
      <a-button type="primary" @click="$emit('back')">返回列表</a-button>
    </div>

    <div v-else class="file-content-container">
      <!-- PDF Viewer -->
      <div v-if="fileType === 'pdf'" class="pdf-container">
        <AdvancedPDFReader
          :file="fileData"
          :initial-annotations="annotations"
          :current-mode="currentMode"
          :selected-color="selectedColor"
          :current-page="currentPage"
          :num-pages="numPages"
          :scale="scale"
          :on-page-change="handlePageChange"
          :on-num-pages-change="handleNumPagesChange"
          :on-scale-change="handleScaleChange"
          :on-highlight-click="onHighlightClick"
          :show-highlights="showHighlights"
          :is-reading="isReading"
          :reading-speed="readingSpeed"
          :reading-range="readingRange"
          :on-reading-status-change="onReadingStatusChange"
          :on-reading-progress-change="onReadingProgressChange"
          :on-reading-text-change="onReadingTextChange"
        />
      </div>

      <!-- Image Viewer -->
      <div v-else-if="fileType === 'image'" class="image-viewer">
        <div class="image-container" @wheel="handleImageWheel">
          <img 
            :src="fileData" 
            :style="{ transform: `scale(${imageScale})`}"
            @load="onImageLoad"
            alt="图片预览"
          />
        </div>
      </div>

      <!-- Text Viewer -->
      <div v-else-if="fileType === 'text'" class="text-viewer">
        <div class="search-controls" v-if="textSearchQuery && textSearchMatches.length > 0">
          <a-button size="small" @click="prevSearchMatch">
            <UpOutlined />
          </a-button>
          <span>{{ textSearchCurrentIndex + 1 }} / {{ textSearchMatches.length }}</span>
          <a-button size="small" @click="nextSearchMatch">
            <DownOutlined />
          </a-button>
        </div>
        <pre class="text-content" :class="{ 'word-wrap': wordWrap }" v-html="textContentWithHighlight"></pre>
      </div>

      <!-- DOCX Viewer -->
      <div v-else-if="fileType === 'docx'" class="docx-viewer">
        <div class="docx-container" ref="docxContainer" :style="{ transform: `scale(${docxScale})`}"></div>
      </div>

      <!-- XLSX Viewer -->
      <div v-else-if="fileType === 'xlsx'" class="xlsx-viewer">
        <div class="xlsx-container">
          <table class="xlsx-table" v-if="sheetData.length > 0">
            <thead>
              <tr>
                <th v-for="(cell, colIndex) in sheetData[0]" :key="colIndex">
                  {{ getColumnName(colIndex) }}
                </th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="(row, rowIndex) in sheetData" :key="rowIndex">
                <td 
                  v-for="(cell, colIndex) in row" 
                  :key="colIndex"
                  :class="getCellClass(cell)"
                >
                  {{ formatCellValue(cell) }}
                </td>
              </tr>
            </tbody>
          </table>
          <div v-else class="empty-sheet">此工作表没有数据</div>
        </div>
      </div>

      <!-- Unsupported Format -->
      <div v-else class="unsupported-viewer">
        <FileUnknownOutlined style="font-size: 64px; color: #faad14;" />
        <h2>不支持的文件格式</h2>
        <p>当前文件格式: {{ fileName || fileExtension || '未知' }}</p>
        <p class="supported-list">
          <span>支持格式：</span>
          <span>• PDF (.pdf)</span>
          <span>• Word (.docx, .doc)</span>
          <span>• Excel (.xlsx, .xls)</span>
          <span>• 图片 (.jpg, .jpeg, .png, .gif, .webp, .bmp, .svg)</span>
          <span>• 文本 (.txt, .json, .xml, .csv, .md, .html, .log, .js, .css)</span>
        </p>
        <a-button type="primary" @click="downloadFile">
          <DownloadOutlined /> 下载文件
        </a-button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, watch, onUnmounted, nextTick } from 'vue'
import { message } from 'ant-design-vue'
import { 
  CloseCircleOutlined, 
  CopyOutlined, 
  DownloadOutlined,
  FileUnknownOutlined,
  SearchOutlined,
  PlusOutlined,
  MinusOutlined,
  UpOutlined,
  DownOutlined
} from '@ant-design/icons-vue'
import AdvancedPDFReader from './pdfReader/AdvancedPDFReader.vue'
import { fileAPI } from '../api/file.js'
import * as XLSX from 'xlsx'
import { renderAsync } from 'docx-preview'

const props = defineProps({
  fileId: {
    type: [Number, String],
    required: true
  },
  fileName: {
    type: String,
    default: ''
  },
  fileExtension: {
    type: String,
    default: ''
  },
  annotations: {
    type: Object,
    default: null
  },
  currentMode: {
    type: String,
    default: 'comment'
  },
  selectedColor: {
    type: String,
    default: null
  },
  currentPage: {
    type: Number,
    default: 1
  },
  numPages: {
    type: Number,
    default: 1
  },
  scale: {
    type: Number,
    default: 1
  },
  showHighlights: {
    type: Boolean,
    default: true
  },
  isReading: {
    type: Boolean,
    default: false
  },
  readingSpeed: {
    type: Number,
    default: 1
  },
  readingRange: {
    type: String,
    default: 'current'
  }
})

const emit = defineEmits([
  'back',
  'page-change',
  'num-pages-change',
  'scale-change',
  'annotations-change',
  'highlight-click',
  'reading-status-change',
  'reading-progress-change',
  'reading-text-change'
])

const loading = ref(true)
const error = ref(null)
const fileData = ref(null)
const textContent = ref('')
const fileType = ref('unsupported')

const imageScale = ref(1)
const docxScale = ref(1)
const wordWrap = ref(true)

const docxContainer = ref(null)

const sheetNames = ref([])
const sheetData = ref([])
const activeSheet = ref('')

const textSearchQuery = ref('')
const textSearchMatches = ref([])
const textSearchCurrentIndex = ref(0)

const SUPPORTED_PDF = ['.pdf']
const SUPPORTED_IMAGES = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg']
const SUPPORTED_DOCX = ['.docx', '.doc']
const SUPPORTED_XLSX = ['.xlsx', '.xls']
const SUPPORTED_TXT = ['.txt', '.json', '.xml', '.csv', '.md', '.html', '.htm', '.log', '.js', '.css']

const getFileType = (ext) => {
  let lowerExt = (ext || '').toLowerCase()
  // 确保扩展名有前缀点
  if (lowerExt && !lowerExt.startsWith('.')) {
    lowerExt = '.' + lowerExt
  }
  console.log('getFileType 输入:', ext, '处理后:', lowerExt)
  if (SUPPORTED_PDF.includes(lowerExt)) return 'pdf'
  if (SUPPORTED_IMAGES.includes(lowerExt)) return 'image'
  if (SUPPORTED_TXT.includes(lowerExt)) return 'text'
  if (SUPPORTED_DOCX.includes(lowerExt)) return 'docx'
  if (SUPPORTED_XLSX.includes(lowerExt)) return 'xlsx'
  return 'unsupported'
}

const getFileExtension = (filename) => {
  if (!filename) return ''
  const lastDot = filename.lastIndexOf('.')
  return lastDot !== -1 ? filename.substring(lastDot).toLowerCase() : ''
}

const textContentWithHighlight = computed(() => {
  if (!textSearchQuery.value || textSearchQuery.value.trim() === '') {
    return escapeHtml(textContent.value)
  }
  
  let content = escapeHtml(textContent.value)
  const query = escapeRegExp(textSearchQuery.value)
  const regex = new RegExp(query, 'gi')
  return content.replace(regex, (match, offset) => {
    const idx = textSearchMatches.value.findIndex(m => m.start === offset)
    if (idx !== -1 && idx === textSearchCurrentIndex.value) {
      return `<mark class="current-match">${match}</mark>`
    } else if (idx !== -1) {
      return `<mark>${match}</mark>`
    }
    return match
  })
})

const escapeHtml = (text) => {
  const div = document.createElement('div')
  div.textContent = text
  return div.innerHTML
}

const escapeRegExp = (string) => {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

const loadFile = async () => {
  loading.value = true
  error.value = null
  fileData.value = null
  textContent.value = ''
  textSearchQuery.value = ''
  textSearchMatches.value = []
  textSearchCurrentIndex.value = 0
  sheetNames.value = []
  sheetData.value = []

  try {
    const ext = props.fileExtension || getFileExtension(props.fileName)
    fileType.value = getFileType(ext)

    if (fileType.value === 'unsupported') {
      loading.value = false
      return
    }

    if (fileType.value === 'text') {
      const response = await fileAPI.getTextFileContent(props.fileId)
      if (response.data?.code === 200) {
        textContent.value = response.data.data
      } else {
        throw new Error(response.data?.message || '获取文本内容失败')
      }
    } else if (fileType.value === 'xlsx') {
      const response = await fileAPI.previewFile(props.fileId)
      const data = await response.data.arrayBuffer()
      loading.value = false
      await nextTick()
      parseExcelFile(data)
      return
    } else if (fileType.value === 'docx') {
      console.log('开始加载DOCX文件')
      const response = await fileAPI.previewFile(props.fileId)
      console.log('API响应:', response)
      console.log('响应数据类型:', typeof response.data)
      console.log('响应数据size:', response.data.size)
      console.log('响应数据是Blob吗?:', response.data instanceof Blob)
      // 先将 loading 设为 false, 让模板渲染容器！
      loading.value = false
      await nextTick()
      console.log('docxContainer.value:', docxContainer.value)
      await renderDocxFile(response.data)
      return // 已经设为 false, 不需要后面再设了！
    } else {
      const response = await fileAPI.previewFile(props.fileId)
      const blobUrl = URL.createObjectURL(response.data)
      fileData.value = blobUrl
    }

    loading.value = false
  } catch (err) {
      console.error('加载文件失败:', err)
      error.value = err.message || '加载文件失败'
      loading.value = false
    }
}

const searchText = () => {
  if (!textSearchQuery.value || textSearchQuery.value.trim() === '') {
    textSearchMatches.value = []
    textSearchCurrentIndex.value = 0
    return
  }

  const query = textSearchQuery.value.trim()
  const regex = new RegExp(escapeRegExp(query), 'gi')
  const matches = []
  let match
  while ((match = regex.exec(textContent.value)) !== null) {
    matches.push({
      start: match.index,
      end: regex.lastIndex
    })
  }
  textSearchMatches.value = matches
  textSearchCurrentIndex.value = 0
  if (matches.length > 0) {
    scrollToCurrentMatch()
  }
}

const prevSearchMatch = () => {
  if (textSearchMatches.value.length === 0) return
  textSearchCurrentIndex.value = (textSearchCurrentIndex.value - 1 + textSearchMatches.value.length) % textSearchMatches.value.length
  scrollToCurrentMatch()
}

const nextSearchMatch = () => {
  if (textSearchMatches.value.length === 0) return
  textSearchCurrentIndex.value = (textSearchCurrentIndex.value + 1) % textSearchMatches.value.length
  scrollToCurrentMatch()
}

const scrollToCurrentMatch = () => {
  nextTick(() => {
    const marks = document.querySelectorAll('.text-content .current-match')
    if (marks.length > 0) {
      marks[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
    }
  })
}

const renderDocxFile = async (data) => {
  console.log('renderDocxFile 开始执行')
  console.log('传入的数据:', data)
  console.log('数据类型:', typeof data)
  if (!docxContainer.value) {
    console.log('docxContainer 还未准备好，等待 nextTick')
    await nextTick()
  }
  
  console.log('docxContainer.value 现在:', docxContainer.value)
  if (docxContainer.value) {
    docxContainer.value.innerHTML = ''
    console.log('docxContainer 已清空')
    
    try {
      console.log('开始调用 renderAsync')
      console.log('renderAsync:', renderAsync)
      await renderAsync(data, docxContainer.value, null, {
        className: 'docx-content',
        inWrapper: true,
        ignoreWidth: true,
        ignoreHeight: false,
        ignoreFonts: false,
        breakPages: true,
        ignoreLastRenderedPageBreak: true,
        experimental: false,
        trimXmlDeclaration: true,
        useBase64URL: true,
        renderHeaders: true,
        renderFooters: true,
        renderFootnotes: true,
        renderEndnotes: true
      })
      console.log('renderAsync 执行完成')
      console.log('docxContainer.innerHTML 长度:', docxContainer.value.innerHTML.length)
      console.log('docxContainer.innerHTML 前200字符:', docxContainer.value.innerHTML.substring(0, 200))
    } catch (err) {
      console.error('渲染DOCX失败:', err)
      console.error('错误堆栈:', err.stack)
      throw new Error('DOCX文件渲染失败: ' + err.message)
    }
  } else {
    console.error('无法获取 docxContainer 引用')
  }
}

const parseExcelFile = (arrayBuffer) => {
  try {
    const workbook = XLSX.read(arrayBuffer, { type: 'array' })
    sheetNames.value = workbook.SheetNames
    
    if (sheetNames.value.length > 0) {
      activeSheet.value = sheetNames.value[0]
      loadSheet(activeSheet.value, workbook)
    }
  } catch (err) {
    console.error('解析Excel失败:', err)
    throw new Error('Excel文件解析失败')
  }
}

const loadSheet = (sheetName, workbook) => {
  if (!workbook) {
    const data = localStorage.getItem(`xlsx_${props.fileId}`)
    if (data) {
      workbook = XLSX.read(new Uint8Array(JSON.parse(data)), { type: 'array' })
    }
  }
  
  if (workbook && workbook.Sheets[sheetName]) {
    const sheet = workbook.Sheets[sheetName]
    sheetData.value = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: '' })
  }
}

const getColumnName = (index) => {
  let name = ''
  let num = index
  while (num >= 0) {
    name = String.fromCharCode(65 + (num % 26)) + name
    num = Math.floor(num / 26) - 1
  }
  return name
}

const formatCellValue = (cell) => {
  if (cell === null || cell === undefined) return ''
  if (typeof cell === 'object') {
    return cell.v || cell.w || JSON.stringify(cell)
  }
  return String(cell)
}

const getCellClass = (cell) => {
  if (typeof cell === 'number') return 'numeric'
  return ''
}

const zoomIn = () => {
  if (fileType.value === 'image') {
    imageScale.value = Math.min(imageScale.value + 0.25, 3)
  } else if (fileType.value === 'docx') {
    docxScale.value = Math.min(docxScale.value + 0.1, 2)
  }
}

const zoomOut = () => {
  if (fileType.value === 'image') {
    imageScale.value = Math.max(imageScale.value - 0.25, 0.25)
  } else if (fileType.value === 'docx') {
    docxScale.value = Math.max(docxScale.value - 0.1, 0.5)
  }
}

const resetImageZoom = () => {
  imageScale.value = 1
}

const handleImageWheel = (e) => {
  if (e.ctrlKey) {
    e.preventDefault()
    if (e.deltaY < 0) {
      zoomIn()
    } else {
      zoomOut()
    }
  }
}

const onImageLoad = () => {
  console.log('图片加载完成')
}

const copyText = async () => {
  try {
    await navigator.clipboard.writeText(textContent.value)
    message.success('内容已复制到剪贴板')
  } catch (err) {
    message.error('复制失败')
  }
}

const toggleWordWrap = () => {
  wordWrap.value = !wordWrap.value
}

const downloadFile = async () => {
  try {
    const response = await fileAPI.downloadFile(props.fileId)
    const blob = new Blob([response.data])
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = props.fileName || 'download'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    URL.revokeObjectURL(url)
  } catch (err) {
    message.error('下载失败')
  }
}

const handlePageChange = (page) => {
  emit('page-change', page)
}

const handleNumPagesChange = (pages) => {
  emit('num-pages-change', pages)
}

const handleScaleChange = (newScale) => {
  emit('scale-change', newScale)
}

const onAnnotationsChange = (data) => {
  emit('annotations-change', data)
}

const onHighlightClick = (annotation) => {
  emit('highlight-click', annotation)
}

const onReadingStatusChange = (status) => {
  emit('reading-status-change', status)
}

const onReadingProgressChange = (progress) => {
  emit('reading-progress-change', progress)
}

const onReadingTextChange = (text) => {
  emit('reading-text-change', text)
}

watch(() => props.fileId, () => {
  loadFile()
}, { immediate: true })

const setActiveSheet = (sheetName) => {
  if (sheetNames.value.includes(sheetName)) {
    activeSheet.value = sheetName
  }
}

watch(activeSheet, (newSheet) => {
  if (newSheet) {
    const cachedData = localStorage.getItem(`xlsx_workbook_${props.fileId}`)
    if (cachedData) {
      const workbook = XLSX.read(new Uint8Array(JSON.parse(cachedData)), { type: 'array' })
      loadSheet(newSheet, workbook)
    }
  }
})

onUnmounted(() => {
  if (fileData.value && fileData.value.startsWith('blob:')) {
    URL.revokeObjectURL(fileData.value)
  }
})

// 对外暴露方法和属性
defineExpose({
  fileType,
  imageScale,
  docxScale,
  activeSheet,
  sheetNames,
  textSearchQuery,
  wordWrap,
  zoomIn,
  zoomOut,
  resetImageZoom,
  searchText,
  prevSearchMatch,
  nextSearchMatch,
  copyText,
  toggleWordWrap,
  setActiveSheet
})
</script>

<style scoped>
.universal-file-reader {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: #f5f5f5;
}

.loading-container,
.error-container,
.unsupported-viewer {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 40px;
  text-align: center;
}

.loading-container p,
.error-container p {
  margin-top: 16px;
  color: #666;
}

.error-container h2,
.unsupported-viewer h2 {
  margin-top: 16px;
  color: #333;
}

.unsupported-viewer p {
  color: #666;
}

.unsupported-viewer .supported-list {
  color: #999;
  font-size: 13px;
  margin: 16px 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.file-content-container {
  flex: 1;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.pdf-container {
  flex: 1;
  overflow: hidden;
  height: 100%;
}

.image-viewer {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #2c2c2c;
}

.image-toolbar,
.docx-toolbar,
.xlsx-toolbar,
.text-toolbar {
  display: none !important;
}

.image-toolbar {
  justify-content: center;
}

.image-toolbar .zoom-level,
.docx-toolbar .zoom-level {
  min-width: 50px;
  text-align: center;
  font-weight: 500;
}

.image-container {
  flex: 1;
  overflow: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.image-container img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  transition: transform 0.2s ease;
}

.text-viewer {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #fff;
}

.toolbar-actions {
  display: flex;
  gap: 8px;
}

.search-controls {
  display: flex;
  gap: 8px;
  padding: 8px 16px;
  background: #fafafa;
  border-bottom: 1px solid #e8e8e8;
}

.text-content {
  flex: 1;
  margin: 0;
  padding: 20px;
  overflow: auto;
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 14px;
  line-height: 1.6;
  background: #fafafa;
  white-space: pre;
  tab-size: 4;
}

.text-content.word-wrap {
  white-space: pre-wrap;
  word-wrap: break-word;
}

.text-content mark {
  background: #ffeb3b;
  padding: 0 2px;
  border-radius: 2px;
}

.text-content .current-match {
  background: #ff9800;
}

.docx-viewer {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #f0f0f0;
}

.docx-container {
  flex: 1;
  overflow: auto;
  padding: 20px;
  background: #f0f0f0;
  display: flex;
  justify-content: center;
}

.docx-container :deep(.docx-content) {
  background: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  margin: 0 auto;
}

.xlsx-viewer {
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #fff;
}

.xlsx-container {
  flex: 1;
  overflow: auto;
  padding: 16px;
}

.xlsx-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.xlsx-table th,
.xlsx-table td {
  border: 1px solid #e8e8e8;
  padding: 8px 12px;
  text-align: left;
  max-width: 300px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.xlsx-table th {
  background: #fafafa;
  font-weight: 600;
  position: sticky;
  top: 0;
  z-index: 1;
}

.xlsx-table td.numeric {
  text-align: right;
  font-family: 'Consolas', monospace;
}

.xlsx-table tr:hover td {
  background: #f5f5f5;
}

.empty-sheet {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 200px;
  color: #999;
}
</style>
