<template>
  <div ref="panelRef" class="file-editor-panel" :style="{ width: width + '%' }">
    <!-- 有选中文件时显示表头和内容 -->
    <template v-if="config.currentFileName">
    <div class="panel-header">
      <span class="panel-title">
        <template v-if="config.isImageView">🖼️ 图片预览</template>
        <template v-else-if="config.isVideoView">🎬 视频预览</template>
        <template v-else-if="config.isAudioView">🎵 音频预览</template>
        <template v-else-if="config.isPdfFile">📕 PDF 预览</template>
        <template v-else-if="config.isHtmlFile">🌐 HTML 预览</template>
        <template v-else-if="config.isMarkdownFile">📝 Markdown 预览</template>
        <template v-else-if="config.isExcelFile">📊 Excel 预览</template>
        <template v-else-if="config.isWordFile">📄 Word 预览</template>
        <template v-else-if="config.isCsvFile">📋 CSV 预览</template>
        <template v-else>📝 文件内容</template>
      </span>
      <div class="header-actions">
        <a-tooltip v-if="config.currentFileName" content="全屏预览 (F11)" position="left">
          <a-button size="mini" type="text" @click="toggleFullscreen">
            <icon-fullscreen v-if="!isFullscreen" />
            <icon-fullscreen-exit v-else />
          </a-button>
        </a-tooltip>
        <div v-if="config.currentFileName" class="filename-with-copy">
        <a-tooltip :content="config.currentFileFullPath" position="left">
          <span
            class="panel-filename"
            :class="{ 'file-outside-dir': !isFileInCurrentDirectory && config.currentFileFullPath }"
          >
            {{ config.currentFileName }}
          </span>
        </a-tooltip>
        <a-tooltip :content="copied ? '已复制' : '复制路径'" position="left">
          <a-button
            size="mini"
            type="text"
            :status="copied ? 'success' : 'normal'"
            @click="handleCopyPath"
          >
          <icon-copy v-if="!copied" />
          <icon-check v-else />
          </a-button>
        </a-tooltip>
      </div>
      </div>
    </div>

    <div class="editor-content thin-dark-scrollbar">
      <!-- 二进制文件提示 -->
      <div v-if="config.isBinaryFile" class="binary-file-message">
        <pre>{{ config.fileContent }}</pre>
      </div>

      <!-- 图片预览 -->
      <div v-else-if="config.isImageView" class="media-preview">
        <img
          :src="config.previewUrl"
          class="preview-image"
          @load="handleImageLoad"
          @error="handleImageError"
          alt="预览"
        />
        <div v-if="config.imageLoading" class="media-loading">
          <a-spin />
        </div>
        <div class="media-meta">
          <span class="file-name">{{ getFileName(config.currentFileFullPath) }}</span>
          <span v-if="config.currentImageDimensions" class="image-dimensions">
            {{ config.currentImageDimensions }}
          </span>
        </div>
      </div>

      <!-- 视频预览 -->
      <div v-else-if="config.isVideoView" class="media-preview">
        <video :src="config.previewUrl" controls class="preview-video" @error="handleMediaError('视频')" @canplay="mediaErrorMsg = ''"></video>
        <div v-if="mediaErrorMsg" class="media-error">{{ mediaErrorMsg }}</div>
        <div class="media-meta">
          <a-tag color="arcoblue">🎬 视频</a-tag>
        </div>
      </div>

      <!-- 音频预览 -->
      <div v-else-if="config.isAudioView" class="media-preview">
        <audio :src="config.previewUrl" controls class="preview-audio" @error="handleMediaError('音频')" @canplay="mediaErrorMsg = ''"></audio>
        <div v-if="mediaErrorMsg" class="media-error">{{ mediaErrorMsg }}</div>
        <div class="media-meta">
          <a-tag color="green">🎵 音频</a-tag>
        </div>
      </div>

      <!-- PDF 预览 -->
      <div v-else-if="config.isPdfFile" class="media-preview media-preview-pdf">
        <iframe :src="config.previewUrl" class="preview-pdf" @load="handlePdfLoad"></iframe>
        <div v-if="mediaErrorMsg" class="media-error">{{ mediaErrorMsg }}</div>
        <div class="media-meta">
          <a-tag color="orangered">📕 PDF</a-tag>
        </div>
      </div>

      <!-- Excel 预览 -->
      <div v-else-if="config.isExcelFile" class="office-preview">
        <div class="office-preview-container" ref="excelPreviewRef">
          <a-spin v-if="config.officeLoading" :loading="true" tip="加载中...">
            <div class="loading-placeholder"></div>
          </a-spin>
          <a-alert v-else-if="config.officeError" type="error" :message="config.officeError" />
        </div>
      </div>

      <!-- Word 预览 -->
      <div v-else-if="config.isWordFile" class="office-preview">
        <div class="office-preview-container" ref="wordPreviewRef">
          <a-spin v-if="config.officeLoading" :loading="true" tip="加载中...">
            <div class="loading-placeholder"></div>
          </a-spin>
          <a-alert v-else-if="config.officeError" type="error" :message="config.officeError" />
        </div>
      </div>

      <!-- CSV 预览/编辑 -->
      <div v-else-if="config.isCsvFile" class="csv-preview-wrapper">
        <div class="preview-mode-switch">
          <a-tooltip v-if="config.isEditMode && config.canResetContent" position="left" content="恢复原始内容">
            <a-button type="outline" size="small" @click="handleReset">
              <template #icon><icon-undo /></template>
            </a-button>
          </a-tooltip>
          <a-tooltip v-if="config.canSaveFile" position="left" content="保存 (Ctrl+S)">
            <a-button type="primary" size="small" @click="handleSave">
              <template #icon><icon-save /></template>
            </a-button>
          </a-tooltip>
          <a-tooltip v-if="!config.isEditMode" position="left" content="导出 PDF">
            <a-button type="outline" size="small" @click="handleExportCsvPDF">
              <template #icon><icon-file-pdf /></template>
            </a-button>
          </a-tooltip>
          <a-tooltip position="left" :content="getModeSwitchTooltip()">
            <a-button type="primary" size="small" @click="handleToggleEditMode">
              <template #icon>
                <icon-eye v-if="config.isEditMode" />
                <icon-edit v-else />
              </template>
            </a-button>
          </a-tooltip>
        </div>

        <!-- 预览模式 -->
        <div v-if="!config.isEditMode" class="office-preview-container" ref="csvPreviewRef">
          <a-spin v-if="config.officeLoading" :loading="true" tip="加载中...">
            <div class="loading-placeholder"></div>
          </a-spin>
          <a-alert v-else-if="config.officeError" type="error" :message="config.officeError" />
        </div>

        <!-- 编辑模式 -->
        <div v-else class="csv-edit-wrapper">
          <AsyncCodeEditor
            :model-value="config.fileContent"
            :file-extension="config.currentFileExtension"
            :file-path="config.currentFileFullPath"
            :file-mtime="config.fileMtime"
            @update:model-value="handleContentUpdate"
            class="code-editor"
          />
          <div class="resize-handle-v" @mousedown="handleStartResize" title="拖拽调整高度">
            <div class="resize-dots"></div>
          </div>
        </div>
      </div>

      <!-- HTML 预览/编辑 -->
      <div v-else-if="config.isHtmlFile" class="html-preview-wrapper">
        <!-- 编辑模式/预览模式切换按钮 -->
        <div class="preview-mode-switch">
          <!-- 重置按钮（编辑模式且内容变化时显示） -->
          <a-tooltip v-if="config.isEditMode && config.canResetContent" position="left" content="恢复原始内容">
            <a-button
              type="outline"
              size="small"
              @click="handleReset"
            >
              <template #icon><icon-undo /></template>
            </a-button>
          </a-tooltip>
          <!-- 保存按钮（仅内容变化时显示） -->
          <a-tooltip v-if="config.canSaveFile" position="left" content="保存 (Ctrl+S)">
            <a-button
              type="primary"
              size="small"
              @click="handleSave"
            >
              <template #icon><icon-save /></template>
            </a-button>
          </a-tooltip>
          <!-- 预览/编辑切换按钮 -->
          <a-tooltip
            position="left"
            :content="getModeSwitchTooltip()"
          >
            <a-button
              type="primary"
              size="small"
              @click="handleToggleEditMode"
            >
              <template #icon>
                <icon-eye v-if="config.isEditMode" />
                <icon-edit v-else />
              </template>
            </a-button>
          </a-tooltip>
        </div>

        <!-- 预览模式 -->
        <iframe
          v-if="!config.isEditMode"
          class="html-preview-content"
          :src="htmlPreviewUrl"
        ></iframe>

        <!-- 编辑模式 -->
        <div v-else class="html-edit-wrapper">
          <AsyncCodeEditor
            :model-value="config.fileContent"
            :file-extension="config.currentFileExtension"
            :file-path="config.currentFileFullPath"
            :file-mtime="config.fileMtime"
            @update:model-value="handleContentUpdate"
            class="code-editor"
          />
        </div>
      </div>

      <!-- Markdown 预览/编辑 -->
      <div v-else-if="config.isMarkdownFile" class="markdown-preview-wrapper">
        <!-- 编辑模式/预览模式切换按钮 -->
        <div class="preview-mode-switch">
          <!-- 重置按钮（编辑模式且内容变化时显示） -->
          <a-tooltip v-if="config.isEditMode && config.canResetContent" position="left" content="恢复原始内容">
            <a-button
              type="outline"
              size="small"
              @click="handleReset"
            >
              <template #icon><icon-undo /></template>
            </a-button>
          </a-tooltip>
          <!-- 保存按钮（仅内容变化时显示） -->
          <a-tooltip v-if="config.canSaveFile" position="left" content="保存 (Ctrl+S)">
            <a-button
              type="primary"
              size="small"
              @click="handleSave"
            >
              <template #icon><icon-save /></template>
            </a-button>
          </a-tooltip>
          <!-- PDF 导出按钮（仅在预览模式显示） -->
          <a-tooltip v-if="!config.isEditMode" position="left" content="导出">
            <a-button
              type="outline"
              size="small"
              @click="handleExportPDF"
            >
              <template #icon><icon-file-pdf /></template>
            </a-button>
          </a-tooltip>
          <!-- 预览/编辑切换按钮 -->
          <a-tooltip
            position="left"
            :content="getModeSwitchTooltip()"
          >
            <a-button
              type="primary"
              size="small"
              @click="handleToggleEditMode"
            >
              <template #icon>
                <icon-eye v-if="config.isEditMode" />
                <icon-edit v-else />
              </template>
            </a-button>
          </a-tooltip>
        </div>

        <!-- 预览模式 -->
        <div v-if="!config.isEditMode" ref="markdownPreviewRef" class="markdown-preview-content markdown-content thin-dark-scrollbar" v-html="config.rendered"></div>

        <!-- 编辑模式 -->
        <div v-else class="markdown-edit-wrapper">
          <AsyncCodeEditor
            :model-value="config.fileContent"
            :file-extension="config.currentFileExtension"
            :file-path="config.currentFileFullPath"
            :file-mtime="config.fileMtime"
            @update:model-value="handleContentUpdate"
            class="code-editor"
          />
          <!-- 调整高度的手柄 -->
          <div class="resize-handle-v" @mousedown="handleStartResize" title="拖拽调整高度">
            <div class="resize-dots"></div>
          </div>
        </div>
      </div>

      <!-- 文本编辑器（带代码高亮） -->
      <div v-else class="text-editor-wrapper">
        <!-- 编辑操作按钮 -->
        <div class="preview-mode-switch">
          <!-- 重置按钮（内容变化时显示） -->
          <a-tooltip v-if="config.canResetContent" position="left" content="恢复原始内容">
            <a-button
              type="outline"
              size="small"
              @click="handleReset"
            >
              <template #icon><icon-undo /></template>
            </a-button>
          </a-tooltip>
          <!-- 保存按钮（内容变化时显示） -->
          <a-tooltip v-if="config.canSaveFile" position="left" content="保存 (Ctrl+S)">
            <a-button
              type="primary"
              size="small"
              @click="handleSave"
            >
              <template #icon><icon-save /></template>
            </a-button>
          </a-tooltip>
          <!-- 预览按钮（始终显示，但根据文件类型决定是否可用） -->
          <a-tooltip
            position="left"
            :content="getPreviewButtonTooltip()"
          >
            <a-button
              type="primary"
              size="small"
              :disabled="!config.canPreviewFile"
              @click="handleToggleEditMode"
            >
              <template #icon><icon-eye /></template>
            </a-button>
          </a-tooltip>
        </div>

        <AsyncCodeEditor
          :model-value="config.fileContent"
          :file-extension="config.currentFileExtension"
          :file-path="config.currentFileFullPath"
          :file-mtime="config.fileMtime"
          @update:model-value="handleContentUpdate"
          class="code-editor"
        />
        <!-- 调整高度的手柄 -->
        <div class="resize-handle-v" @mousedown="handleStartResize" title="拖拽调整高度">
          <div class="resize-dots"></div>
        </div>
      </div>
    </div>
    </template>

    <!-- 多文件预览浮动 Tab 栏 -->
    <div
      v-if="previewTabs.length > 1"
      class="preview-tabs"
    >
      <div
        v-for="tab in previewTabs"
        :key="tab.id"
        class="preview-tab"
        :class="{ active: tab.id === activeTabId }"
        @click="emit('switchTab', tab.id)"
        @mousedown.middle.prevent="emit('closeTab', tab.id)"
      >
        <span class="tab-icon">
          {{ getFileTypeIcon(tab.fileItem.name) }}
          <i v-if="isDirty(tab)" class="tab-dirty"></i>
        </span>
        <span class="tab-name" :title="tab.fileItem.name">{{ tab.fileItem.name }}</span>
        <div class="tab-actions">
          <button
            class="tab-close"
            @click.stop="emit('closeTab', tab.id)"
            title="关闭"
          >×</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, watch, nextTick, defineAsyncComponent, ref, onMounted, onUnmounted } from 'vue'
import { Message } from '@arco-design/web-vue'
import { IconSave, IconEdit, IconEye, IconUndo, IconCopy, IconCheck, IconFilePdf, IconFullscreen, IconFullscreenExit } from '@arco-design/web-vue/es/icon'
import { getFileName, escapeHtml } from '@/utils/fileUtils'
import { useClipboardCopy } from '../composables/useClipboardCopy'
import type { FileEditorPanelConfig } from '@/types/file-system'
import { renderMermaidDiagrams, rerenderMermaidDiagrams } from '@/utils/markedExtensions'
import { useThemeStore } from '@/stores/theme'
import { previewExcel, previewWord, previewCsv } from '@/utils/filePreviewHandlers'
import { getFileCategory } from '@/utils/fileTypeHelpers'
import { isDirty } from '../composables/useMultiPreview'
import { connectionManager } from '@/api/connection-manager'

// 异步加载 CodeEditor 组件，减少初始包大小
const AsyncCodeEditor = defineAsyncComponent({
  loader: () => import('@/components/CodeEditor.vue'),
  delay: 200,
  timeout: 10000
})

// Office 预览容器引用
const excelPreviewRef = ref<HTMLElement | null>(null)
const wordPreviewRef = ref<HTMLElement | null>(null)
const csvPreviewRef = ref<HTMLElement | null>(null)

// Markdown 预览容器引用
const markdownPreviewRef = ref<HTMLElement | null>(null)

// 全屏
const panelRef = ref<HTMLElement | null>(null)
const isFullscreen = ref(false)

function toggleFullscreen() {
  if (!panelRef.value) return
  if (!document.fullscreenElement) {
    panelRef.value.requestFullscreen().then(() => { isFullscreen.value = true })
  } else {
    document.exitFullscreen().then(() => { isFullscreen.value = false })
  }
}

function onFullscreenChange() {
  isFullscreen.value = !!document.fullscreenElement
}

function onKeyDown(e: KeyboardEvent) {
  if (e.key === 'F11' && props.config.currentFileName) {
    e.preventDefault()
    toggleFullscreen()
  }
}

// Props
interface Props {
  config: FileEditorPanelConfig
  width: number
  currentDirectory?: string
}

const props = withDefaults(defineProps<Props>(), {
  currentDirectory: ''
})

// Emits
interface Emits {
  (e: 'save'): void
  (e: 'reset'): void
  (e: 'toggleEditMode'): void
  (e: 'startResize', event: MouseEvent): void
  (e: 'contentUpdate', content: string): void
  (e: 'imageLoad', dimensions: string): void
  (e: 'imageError'): void
  (e: 'openLocalFile', link: string): void
  (e: 'switchTab', tabId: string): void
  (e: 'closeTab', tabId: string): void
}

const emit = defineEmits<Emits>()

// 多 tab 数据
const previewTabs = computed(() => props.config.previewTabs || [])
const activeTabId = computed(() => props.config.activeTabId || null)

// 文件类型图标（基于已有分类逻辑）
const CATEGORY_ICONS: Record<string, string> = {
  image: '🖼️', video: '🎬', audio: '🎵', pdf: '📕',
  html: '🌐', markdown: '📝', text: '⚡', binary: '📦',
  unknown: '📄'
}
const getFileTypeIcon = (filename: string): string => {
  return CATEGORY_ICONS[getFileCategory(filename)] || '📄'
}

// HTML 预览 URL（实时从 connectionManager 读取，不缓存）
function resolveHtmlPreviewBase(): string {
  if (!connectionManager.isRemote()) return 'http://localhost:2652'
  const base = connectionManager.getFileServerBaseURL()
  if (!base) return 'http://localhost:2652'
  // 远程模式需要完整代理路径前缀 /api/v1/proxy/localfs（htmlPreviewUrl 会替换为 html-preview）
  return base.replace(/\/$/, '') + '/api/v1/proxy/localfs'
}

const htmlPreviewUrl = computed(() => {
  if (!props.config.currentFileFullPath || !props.config.isHtmlFile) return ''
  const encodedPath = encodeURIComponent(props.config.currentFileFullPath)
  const isRemote = connectionManager.isRemote()
  const base = resolveHtmlPreviewBase()
  if (isRemote) {
    // 远程模式：走 /api/v1/proxy/html-preview 路由
    const baseUrl = base.replace(/\/proxy\/localfs\/?$/, '')
    return `${baseUrl}/proxy/html-preview?path=${encodedPath}`
  }
  // 本地模式：直连文件服务器
  return `${base}/localfs/html-preview?path=${encodedPath}`
})

// 计算属性：判断文件是否在当前目录
const isFileInCurrentDirectory = computed(() => {
  if (!props.config.currentFileFullPath || !props.currentDirectory) {
    return false
  }

  // 提取文件的父目录
  const lastBackslash = props.config.currentFileFullPath.lastIndexOf('\\')
  const lastSlash = props.config.currentFileFullPath.lastIndexOf('/')
  const lastSeparator = Math.max(lastBackslash, lastSlash)

  if (lastSeparator === -1) {
    return false
  }

  const fileDir = props.config.currentFileFullPath.substring(0, lastSeparator)

  // 标准化路径进行比较（将 \ 替换为 /，移除末尾的 /）
  const fileDirNormalized = fileDir.replace(/\\/g, '/').replace(/\/$/, '')
  const currentPathNormalized = props.currentDirectory.replace(/\\/g, '/').replace(/\/$/, '')

  return fileDirNormalized === currentPathNormalized
})

// 事件处理
const handleSave = () => {
  emit('save')
}

const handleReset = () => {
  emit('reset')
}

const handleToggleEditMode = () => {
  emit('toggleEditMode')
}

const handleStartResize = (event: MouseEvent) => {
  emit('startResize', event)
}

const handleContentUpdate = (content: string) => {
  emit('contentUpdate', content)
}

const handleImageLoad = (event: Event) => {
  const img = event.target as HTMLImageElement
  if (img.naturalWidth && img.naturalHeight) {
    const dimensions = `${img.naturalWidth} × ${img.naturalHeight}`
    emit('imageLoad', dimensions)
  }
}

const handleImageError = () => {
  emit('imageError')
}

const mediaErrorMsg = ref('')
watch(() => props.config.previewUrl, () => { mediaErrorMsg.value = '' })
const handleMediaError = (type: string) => {
  mediaErrorMsg.value = `${type}文件加载失败，请检查网络连接或文件权限`
}
const handlePdfLoad = (event: Event) => {
  const iframe = event.target as HTMLIFrameElement
  try {
    // iframe 加载后检查内容是否为空（401/404 等错误页面通常内容很少）
    if (!iframe.contentDocument || iframe.contentDocument.body.innerHTML.length < 100) {
      mediaErrorMsg.value = 'PDF 文件加载失败，请检查网络连接或文件权限'
    }
  } catch {
    // 跨域时无法访问 contentDocument，忽略
  }
}

// 带认证的 fetch（远程模式自动附加 Bearer token）
const authFetch = async (url: string): Promise<Response> => {
  const token = connectionManager.activeProfile?.token
  const headers: Record<string, string> = {}
  if (token) headers['Authorization'] = `Bearer ${token}`
  return fetch(url, { headers })
}

// 打印窗口导出 PDF 公共函数
const openPrintWindow = (title: string, bodyHtml: string, extraStyle = '') => {
  const printWindow = window.open('', '_blank')
  if (!printWindow) {
    Message.error('无法打开打印窗口，请检查浏览器设置')
    return
  }

  printWindow.document.write(`
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>${escapeHtml(title)}</title>
        <style>
          * { margin: 0; padding: 0; box-sizing: border-box; }
          body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, "Helvetica Neue", Arial, sans-serif;
            font-size: 13px;
            line-height: 1.5;
            color: #333;
            padding: 20px;
          }
          @media print {
            body { padding: 0; }
            @page { margin: 15mm; size: A4; }
          }
          ${extraStyle}
        </style>
      </head>
      <body>${bodyHtml}</body>
    </html>
  `)
  printWindow.document.close()

  setTimeout(() => { printWindow.print() }, 500)
  Message.success('PDF 导出窗口已打开')
}

// Markdown PDF 导出处理
const handleExportPDF = async () => {
  const markdownContent = markdownPreviewRef.value
  if (!markdownContent) {
    Message.error('无法获取 Markdown 内容')
    return
  }

  openPrintWindow(
    props.config.currentFileName || 'Markdown 导出 PDF',
    `<div class="markdown-content">${markdownContent.innerHTML}</div>`,
    `
      .markdown-content { background: white; padding: 20px; max-width: 100%; }
      @media print {
        .markdown-content { padding: 0; }
        .markdown-content h1 { font-size: 24pt; margin-bottom: 12pt; border-bottom: 2px solid #333; }
        .markdown-content h2 { font-size: 18pt; margin-bottom: 10pt; border-bottom: 1px solid #ccc; }
        .markdown-content h3 { font-size: 14pt; margin-bottom: 8pt; }
        .markdown-content p { margin-bottom: 10pt; }
        .markdown-content ul, .markdown-content ol { margin-bottom: 10pt; }
        .markdown-content li { margin-bottom: 4pt; }
        .markdown-content table { border-collapse: collapse; margin-bottom: 12pt; width: 100%; }
        .markdown-content th, .markdown-content td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        .markdown-content th { background-color: #f5f5f5; font-weight: bold; }
        .markdown-content img { max-width: 100%; height: auto; }
        .markdown-content blockquote { border-left: 4px solid #ddd; margin: 16px 0; padding: 10px 20px; color: #666; }
        .markdown-content code { background-color: #f5f5f5; padding: 2px 4px; border-radius: 3px; font-family: 'Consolas', 'Monaco', monospace; }
        .markdown-content pre { background-color: #f5f5f5; padding: 12px; border-radius: 4px; overflow-x: auto; }
        .markdown-content pre code { background-color: transparent; padding: 0; }
      }
    `
  )
}

// CSV PDF 导出处理
const handleExportCsvPDF = async () => {
  const csvContent = csvPreviewRef.value?.querySelector('.csv-content table')
  if (!csvContent) {
    Message.error('无法获取 CSV 内容')
    return
  }

  openPrintWindow(
    props.config.currentFileName || 'CSV 导出 PDF',
    csvContent.outerHTML,
    `
      table { border-collapse: collapse; width: 100%; margin-bottom: 12pt; page-break-inside: auto; }
      tr { page-break-inside: avoid; page-break-after: auto; }
      th, td { border: 1px solid #dfe2e5; padding: 6px 10px; text-align: left; white-space: nowrap; }
      th { background-color: #f6f8fa; font-weight: 600; }
      tr:nth-child(even) { background-color: #f8f8f8; }
    `
  )
}

// 监听模式切换，切换到预览模式时渲染 Mermaid 图表
watch(() => props.config.isEditMode, async (newVal, oldVal) => {
  // 从编辑模式切换到预览模式
  if (oldVal && !newVal && props.config.isMarkdownFile) {
    await nextTick()
    try {
      await renderMermaidDiagrams()
    } catch (error) {
      console.error('[FileEditorPanel] Mermaid 渲染失败:', error)
    }
  }
})

// 监听主题变化，重新渲染 mermaid 图表
const themeStore = useThemeStore()
watch(() => themeStore.isDark, async () => {
  if (!props.config.isEditMode && markdownPreviewRef.value) {
    try {
      // 等 DOM 更新完成后再重新渲染
      await nextTick()
      await rerenderMermaidDiagrams(markdownPreviewRef.value)
    } catch { /* 忽略 */ }
  }
})

// 监听 Excel 文件变化，触发预览
watch(() => [props.config.isExcelFile, props.config.currentFileFullPath] as const, async ([isExcel, filePath]) => {
  if (isExcel && filePath && excelPreviewRef.value) {
    await loadExcelPreview(filePath)
  }
}, { immediate: true })

// 监听 Word 文件变化，触发预览
watch(() => [props.config.isWordFile, props.config.currentFileFullPath] as const, async ([isWord, filePath]) => {
  if (isWord && filePath && wordPreviewRef.value) {
    await loadWordPreview(filePath)
  }
}, { immediate: true })

// 监听 CSV 文件变化或编辑模式切换，触发预览
watch(() => [props.config.isCsvFile, props.config.currentFileFullPath, props.config.isEditMode] as const, async ([isCsv, filePath, isEditMode]) => {
  if (isCsv && filePath && !isEditMode) {
    await nextTick()
    if (csvPreviewRef.value) {
      await loadCsvPreview(filePath)
    }
  }
}, { immediate: true })

// Excel 预览加载（直接使用本地文件服务器，秒开）
const loadExcelPreview = async (filePath: string) => {
  if (!excelPreviewRef.value) return

  try {
    excelPreviewRef.value.innerHTML = '<div class="loading-hint">加载中...</div>'

    // 直接从本地文件服务器获取（不走 base64）
    const fileUrl = props.config.previewUrl
    const response = await authFetch(fileUrl)
    const blob = await response.blob()
    const file = new File([blob], getFileName(filePath), { type: blob.type || 'application/vnd.ms-excel' })

    const result = await previewExcel(file, excelPreviewRef.value)
    if (!result.success) {
      throw new Error(result.error || '预览失败')
    }
  } catch (error) {
    console.error('[loadExcelPreview] 错误:', error)
    if (excelPreviewRef.value) {
      excelPreviewRef.value.innerHTML = `
        <div class="preview-error">
          <p>❌ Excel 预览失败</p>
          <p class="error-detail">${error?.message || '未知错误'}</p>
        </div>
      `
    }
  }
}

// Word 预览加载
const loadWordPreview = async (filePath: string) => {
  if (!wordPreviewRef.value) return

  try {
    wordPreviewRef.value.innerHTML = '<div class="loading-hint">加载中...</div>'

    const fileUrl = props.config.previewUrl
    const response = await authFetch(fileUrl)
    const blob = await response.blob()
    const file = new File([blob], getFileName(filePath), { type: blob.type || 'application/vnd.ms-word' })

    const result = await previewWord(file, wordPreviewRef.value)
    if (!result.success) {
      throw new Error(result.error || '预览失败')
    }
  } catch (error) {
    console.error('[loadWordPreview] 错误:', error)
    if (wordPreviewRef.value) {
      wordPreviewRef.value.innerHTML = `
        <div class="preview-error">
          <p>❌ Word 预览失败</p>
          <p class="error-detail">${error?.message || '未知错误'}</p>
        </div>
      `
    }
  }
}

// CSV 预览加载
const loadCsvPreview = async (filePath: string) => {
  if (!csvPreviewRef.value) return

  try {
    csvPreviewRef.value.innerHTML = '<div class="loading-hint">加载中...</div>'

    const blob = props.config.fileContent && !props.config.isBinaryFile
      ? new Blob([props.config.fileContent], { type: 'text/csv' })
      : await (await authFetch(props.config.previewUrl)).blob()
    const file = new File([blob], getFileName(filePath), { type: 'text/csv' })

    const result = await previewCsv(file, csvPreviewRef.value)
    if (!result.success) {
      throw new Error(result.error || '预览失败')
    }
  } catch (error) {
    console.error('[loadCsvPreview] 错误:', error)
    if (csvPreviewRef.value) {
      csvPreviewRef.value.innerHTML = `
        <div class="preview-error">
          <p>❌ CSV 预览失败</p>
          <p class="error-detail">${error?.message || String(error)}</p>
        </div>
      `
    }
  }
}

// 获取模式切换按钮的提示文本
const getModeSwitchTooltip = () => {
  if (props.config.isEditMode) {
    return '切换到预览'
  }
  return '切换到编辑'
}

// 获取预览按钮的提示文本（用于普通文本文件）
const getPreviewButtonTooltip = () => {
  if (!props.config.canPreviewFile) {
    return '该文件类型不支持预览'
  }
  return '切换到预览'
}

// 复制文件路径（带状态反馈）
const { copied, copy: copyPath, cleanup: copyCleanup } = useClipboardCopy()

const handleCopyPath = async () => {
  await copyPath(props.config.currentFileFullPath)
}

// 处理 Markdown 预览中的本地文件链接点击
const handleMarkdownLinkClick = (event: MouseEvent) => {
  const target = event.target as HTMLElement
  const link = target.closest('a[data-local-link]') as HTMLAnchorElement | null

  if (link) {
    event.preventDefault()
    event.stopPropagation()

    const localLink = link.getAttribute('data-local-link')
    if (localLink) {
      emit('openLocalFile', localLink)
    }
  }
}

// 监听预览区域的变化，添加/移除事件监听
watch([markdownPreviewRef, () => props.config.isEditMode], ([refVal, isEditMode], [oldRefVal]) => {
  // 移除旧的监听器
  if (oldRefVal) {
    oldRefVal.removeEventListener('click', handleMarkdownLinkClick)
  }
  // 添加新的监听器（仅在预览模式且有 DOM 元素时）
  if (refVal && !isEditMode) {
    refVal.addEventListener('click', handleMarkdownLinkClick)
  }
}, { immediate: true })

// 处理 HTML iframe 发送的消息（链接点击）
const handleHtmlIframeMessage = (event: MessageEvent) => {
  // 安全检查：接受来自本地文件服务器或同源的消息
  // Wails 应用的 origin 可能是 wails://...，而 iframe 来自 http://localhost:2652
  const allowedOrigins = [
    window.location.origin,
    'null',
    resolveHtmlPreviewBase(),  // 动态：本地 localhost:2652 或远程代理地址
  ]
  if (!allowedOrigins.includes(event.origin)) {
    return
  }
  const data = event.data
  if (data && data.type === 'openLocalFile' && data.path) {
    // 直接传递路径，由父组件处理相对路径解析
    emit('openLocalFile', data.path)
  }
}

// 监听 iframe 的 postMessage + 全屏事件
onMounted(() => {
  window.addEventListener('message', handleHtmlIframeMessage)
  document.addEventListener('fullscreenchange', onFullscreenChange)
  document.addEventListener('keydown', onKeyDown)
})

onUnmounted(() => {
  if (markdownPreviewRef.value) {
    markdownPreviewRef.value.removeEventListener('click', handleMarkdownLinkClick)
  }
  window.removeEventListener('message', handleHtmlIframeMessage)
  document.removeEventListener('fullscreenchange', onFullscreenChange)
  document.removeEventListener('keydown', onKeyDown)
  copyCleanup()
})
</script>

<style scoped>
.file-editor-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--color-bg-1);
  position: relative;
}

.file-editor-panel:fullscreen {
  width: 100vw !important;
  height: 100vh;
  z-index: 1000;
}

.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 3px 12px;
  background: var(--color-bg-2);
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0;
  gap: 12px;
}

.panel-header > * {
  --wails-draggable: no-drag;
}

/* 仅全屏模式下 header 可拖动窗口 */
.file-editor-panel:fullscreen .panel-header {
  --wails-draggable: drag;
}

.panel-title {
  color: var(--color-text-1);
  flex-shrink: 0;
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.filename-with-copy {
  display: flex;
  align-items: center;
  gap: 4px;
}

.panel-filename {
  font-size: 12px;
  color: var(--color-text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 400px;
}

.file-location-hint {
  color: var(--color-text-4);
  font-style: italic;
}

.copy-icon {
  font-size: 14px;
  cursor: pointer;
  color: var(--color-text-3);
  flex-shrink: 0;
}

.copy-icon:hover {
  color: rgb(var(--primary-6));
}

.editor-content {
  flex: 1;
  overflow: auto;
  position: relative;
}

/* 媒体预览 */
.media-preview {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 20px;
}

.preview-image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 8px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.preview-video,
.preview-audio {
  max-width: 100%;
  max-height: 80%;
  border-radius: 8px;
}

.preview-pdf {
  width: 100%;
  height: 100%;
  border: none;
}

.media-preview-pdf {
  padding: 0;
}

.media-loading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.media-error {
  color: var(--color-danger-6);
  font-size: 12px;
  padding: 4px 0;
  text-align: center;
}

.media-meta {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 8px 12px;
  background: var(--color-bg-2);
  border-radius: 6px;
}

.file-name {
  font-size: 12px;
  color: var(--color-text-2);
}

.image-dimensions {
  font-size: 11px;
  color: var(--color-text-3);
}

/* 预览/编辑切换 */
.html-preview-wrapper,
.markdown-preview-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  position: relative;
}

.preview-mode-switch {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 10;
  display: flex;
  gap: 8px;
}

.html-preview-content {
  width: 100%;
  height: 100%;
  border: none;
  background: var(--color-bg-1);
}

.markdown-preview-content {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
  overflow-x: hidden;
  background: var(--color-bg-1);
  color: var(--color-text-1);
  min-height: 0;
  /* 确保内容不会被截断 */
  position: relative;
}

/* Mermaid 图表样式 - 确保不截断后续内容 */
.markdown-preview-content :deep(pre.mermaid) {
  display: block;
  margin: 20px 0;
  background: transparent;
  overflow: visible;
}

.markdown-preview-content :deep(.mermaid) {
  display: block;
  margin: 20px 0;
  overflow: visible;
}

.markdown-preview-content :deep(.mermaid svg) {
  display: block;
  max-width: 100%;
  height: auto;
}

.markdown-preview-content :deep(h1) {
  font-size: 2em;
  margin: 0.5em 0;
  padding-bottom: 0.3em;
  border-bottom: 1px solid var(--color-border-2);
  color: var(--color-text-1);
}

.markdown-preview-content :deep(h2) {
  font-size: 1.5em;
  margin: 0.5em 0;
  padding-bottom: 0.3em;
  border-bottom: 1px solid var(--color-border-2);
  color: var(--color-text-1);
}

.markdown-preview-content :deep(h3),
.markdown-preview-content :deep(h4),
.markdown-preview-content :deep(h5),
.markdown-preview-content :deep(h6) {
  margin: 0.5em 0;
  color: var(--color-text-1);
}

.markdown-preview-content :deep(p) {
  margin: 1em 0;
  line-height: 1.6;
}

.markdown-preview-content :deep(a) {
  color: rgb(var(--primary-6));
  text-decoration: none;
}

.markdown-preview-content :deep(a:hover) {
  text-decoration: underline;
}

/* 本地文件链接样式 */
.markdown-preview-content :deep(a.local-file-link) {
  color: rgb(var(--primary-6));
  cursor: pointer;
  border-bottom: 1px dashed rgb(var(--primary-6));
  text-decoration: none;
}

.markdown-preview-content :deep(a.local-file-link:hover) {
  background-color: rgba(var(--primary-6), 0.1);
}

.markdown-preview-content :deep(code) {
  background: var(--color-fill-2);
  padding: 2px 6px;
  border-radius: 3px;
  font-family: 'Consolas', 'Monaco', monospace;
  font-size: 0.9em;
  color: var(--color-text-1);
}

.markdown-preview-content :deep(pre) {
  background: var(--color-fill-1);
  padding: 12px;
  border-radius: 6px;
  overflow: auto;
  border: 1px solid var(--color-border-2);
}

.markdown-preview-content :deep(pre code) {
  background: none;
  padding: 0;
  color: var(--color-text-1);
}

.markdown-preview-content :deep(blockquote) {
  margin: 1em 0;
  padding-left: 1em;
  border-left: 4px solid var(--primary-6);
  color: var(--color-text-3);
}

.markdown-preview-content :deep(ul),
.markdown-preview-content :deep(ol) {
  margin: 1em 0;
  padding-left: 2em;
}

.markdown-preview-content :deep(li) {
  margin: 0.5em 0;
}

.markdown-preview-content :deep(table) {
  border-collapse: collapse;
  width: 100%;
  margin: 1em 0;
}

.markdown-preview-content :deep(th),
.markdown-preview-content :deep(td) {
  border: 1px solid var(--color-border-2);
  padding: 8px 12px;
}

.markdown-preview-content :deep(th) {
  background: var(--color-fill-2);
  font-weight: 600;
}

.markdown-preview-content :deep(tr:hover) {
  background: var(--color-fill-1);
}

.markdown-preview-content :deep(img) {
  max-width: 100%;
  height: auto;
}

.html-edit-wrapper,
.markdown-edit-wrapper,
.text-editor-wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.code-editor {
  flex: 1;
  min-height: 0;
}

/* 调整手柄 */
.resize-handle-v {
  height: 4px;
  cursor: row-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-fill-2);
  transition: background 0.2s;
  flex-shrink: 0;
}

.resize-handle-v:hover {
  background: var(--color-primary-light-1);
}

.resize-dots {
  display: flex;
  gap: 2px;
}

.resize-dots::before,
.resize-dots::after {
  content: '';
  width: 4px;
  height: 4px;
  background: var(--color-text-3);
  border-radius: 50%;
}

/* 二进制文件提示 */
.binary-file-message {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 20px;
}

/* Office 预览 */
.office-preview {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
  position: relative;
}

/* CSV 预览/编辑 */
.csv-preview-wrapper {
  display: flex;
  flex-direction: column;
  height: 100%;
  position: relative;
}

.csv-edit-wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.office-preview-container {
  flex: 1;
  overflow: auto;
  position: relative;
}

.loading-placeholder {
  width: 100%;
  height: 300px;
}

.preview-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 40px 20px;
  text-align: center;
}

.preview-error p {
  margin: 8px 0;
}

.preview-error .error-detail {
  font-size: 12px;
  color: var(--color-text-3);
  font-family: 'Monaco', 'Menlo', monospace;
}

.preview-error .error-hint {
  font-size: 13px;
  color: var(--color-text-2);
  font-style: italic;
}

.binary-file-message pre {
  margin: 0;
  padding: 20px;
  background: var(--color-fill-1);
  border-radius: 8px;
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 13px;
  line-height: 1.6;
  color: var(--color-text-2);
  white-space: pre;
  overflow-x: auto;
  max-width: 100%;
}

/* ========== Markdown 预览内容样式 ========== */

/* 代码高亮 & Mermaid 图表 - 共享基础样式 */
.markdown-preview-content :deep(.hljs),
.markdown-preview-content :deep(.mermaid) {
  padding: 12px;
  border-radius: 6px;
  overflow: auto;
  font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
  font-size: 14px;
  line-height: 1.5;
}

.markdown-preview-content :deep(.hljs) {
  background: transparent;
}

.markdown-preview-content :deep(pre code.hljs) {
  background: transparent;
  padding: 0;
  color: inherit;
  display: block;
}

.markdown-preview-content :deep(.mermaid text) {
  fill: var(--color-text-1);
}

/* ========== 代码高亮主题色（不依赖 hljs 主题 CSS） ========== */

/* 亮色模式 - GitHub 配色 */
.markdown-preview-content :deep(.hljs) {
  color: #24292e;
  background: #f6f8fa;
}

.markdown-preview-content :deep(.hljs-comment),
.markdown-preview-content :deep(.hljs-quote) { color: #6a737d; font-style: italic; }

.markdown-preview-content :deep(.hljs-keyword),
.markdown-preview-content :deep(.hljs-selector-tag),
.markdown-preview-content :deep(.hljs-subst) { color: #d73a49; }

.markdown-preview-content :deep(.hljs-string),
.markdown-preview-content :deep(.hljs-doctag) { color: #032f62; }

.markdown-preview-content :deep(.hljs-number),
.markdown-preview-content :deep(.hljs-literal),
.markdown-preview-content :deep(.hljs-variable),
.markdown-preview-content :deep(.hljs-template-variable),
.markdown-preview-content :deep(.hljs-tag .hljs-attr) { color: #005cc5; }

.markdown-preview-content :deep(.hljs-title),
.markdown-preview-content :deep(.hljs-section),
.markdown-preview-content :deep(.hljs-selector-id) { color: #6f42c1; font-weight: bold; }

.markdown-preview-content :deep(.hljs-type),
.markdown-preview-content :deep(.hljs-class .hljs-title) { color: #6f42c1; }

.markdown-preview-content :deep(.hljs-tag .hljs-keyword),
.markdown-preview-content :deep(.hljs-tag .hljs-title) { color: #22863a; }

.markdown-preview-content :deep(.hljs-bullet) { color: #e36209; }

.markdown-preview-content :deep(.hljs-symbol) { color: #005cc5; }

.markdown-preview-content :deep(.hljs-built_in),
.markdown-preview-content :deep(.hljs-type) { color: #005cc5; }

.markdown-preview-content :deep(.hljs-attr) { color: #e36209; }

.markdown-preview-content :deep(.hljs-meta) { color: #735c0f; }

.markdown-preview-content :deep(.hljs-addition) { color: #22863a; background-color: #f0fff4; }

.markdown-preview-content :deep(.hljs-deletion) { color: #b31d28; background-color: #ffeef0; }

/* ========== 深色模式适配 ========== */

/* Mermaid 图表深色模式 - 使用原生 dark 主题，仅需背景适配 */
body[arco-theme*='dark'] .markdown-preview-content :deep(.mermaid) {
  background: rgba(255, 255, 255, 0.03);
  border-radius: 8px;
}

/* 代码高亮深色模式 - 使用 CSS 自定义属性 */
body[arco-theme*='dark'] .markdown-preview-content {
  --hljs-string: #98c379;
  --hljs-literal: #98c379;
  --hljs-subst: #98c379;
  --hljs-number: #d19a66;
  --hljs-built_in: #d19a66;
  --hljs-symbol: #61afef;
  --hljs-attr: #e6c07a;
  --hljs-type: #e5c07b;
  --hljs-meta: #7f848e;
  --hljs-deletion: #f56c6c;
  --hljs-addition: #67c23a;
  --hljs-link: #409eff;
}

body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-string) { color: var(--hljs-string); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-literal) { color: var(--hljs-literal); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-subst) { color: var(--hljs-subst); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-number) { color: var(--hljs-number); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-built_in) { color: var(--hljs-built_in); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-symbol) { color: var(--hljs-symbol); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-attr) { color: var(--hljs-attr); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-type) { color: var(--hljs-type); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-meta) { color: var(--hljs-meta); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-deletion) { color: var(--hljs-deletion); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-addition) { color: var(--hljs-addition); }
body[arco-theme*='dark'] .markdown-preview-content :deep(.hljs-link) {
  color: var(--hljs-link);
  text-decoration: underline;
}

/* ========== 多文件预览浮动 Tab 栏 ========== */

.preview-tabs {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  gap: 3px;
  z-index: 20;
  padding: 6px 4px;
  pointer-events: auto;
}

.preview-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  width: 56px;
  padding: 6px 4px 4px;
  border-radius: 8px 0 0 8px;
  background: color-mix(in srgb, var(--color-bg-3) 50%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid color-mix(in srgb, var(--color-border-2) 40%, transparent);
  border-right: none;
  cursor: pointer;
  transition: all 0.3s ease;
  position: relative;
  opacity: 0.68;
}

.preview-tabs:hover .preview-tab {
  opacity: 1;
}

.preview-tab:hover {
  background: var(--color-fill-2);
}

.preview-tab.active {
  background: var(--color-fill-2);
  border-left-color: transparent;
}

.preview-tab.active::before {
  content: '';
  position: absolute;
  left: -1px;
  top: 0;
  bottom: 0;
  width: 3px;
  background: rgb(var(--primary-6));
  border-radius: 3px 0 0 3px;
}

.tab-icon {
  font-size: 16px;
  line-height: 1;
  position: relative;
}

.tab-dirty {
  position: absolute;
  top: -2px;
  left: -14px;
  width: 6px;
  height: 6px;
  background: rgb(var(--warning-6));
  border-radius: 50%;
}

.tab-name {
  font-size: 10px;
  line-height: 1.2;
  color: var(--color-text-2);
  text-align: center;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
}

.preview-tab.active .tab-name {
  color: rgb(var(--primary-6));
  font-weight: 500;
}

.tab-actions {
  position: absolute;
  top: 1px;
  right: 2px;
  display: none;
  align-items: center;
  gap: 1px;
}

.preview-tab:hover .tab-actions {
  display: flex;
}

.tab-close {
  width: 14px;
  height: 14px;
  border: none;
  background: transparent;
  color: var(--color-text-4);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.tab-close:hover:not(:disabled) {
  background: var(--color-fill-3);
  color: var(--color-text-1);
}
</style>
