<template>
  <div class="file-system-container">
    <!-- 主内容区：左侧边栏 + 右侧工作区 -->
    <div class="main-content">
      <!-- 左侧收藏夹侧边栏（全高） -->
      <Sidebar
        v-if="showSidebar"
        :config="sidebarConfig"
        @open-favorite="handleOpenFavorite"
        @remove-favorite="handleRemoveFavorite"
        @toggle-pin="handleTogglePin"
        @long-press-start="handleLongPressStart"
        @long-press-cancel="handleLongPressCancel"
        @drag-start="handleDragStart"
        @drag-over="handleDragOver"
        @drop="handleDrop"
        @drag-end="handleDragEnd"
        @open-connection-dialog="handleOpenConnectionDialog"
      />

      <!-- 右侧工作区：面包屑工具栏 + 文件列表/编辑器 -->
      <div class="workspace-area">
        <!-- 面包屑导航工具栏 -->
        <Toolbar
          ref="toolbarRef"
          :config="toolbarConfig"
          :open-connection-dialog="triggerConnectionDialog"
          :edit-profile-id="pendingEditProfileId"
          @update:file-path="handleFilePathUpdate"
          @update:show-sidebar="handleSidebarToggle"
          @refresh="handleRefresh"
          @exit-zip="handleExitZip"
          @go-to-path="handleGoToPath"
          @open-file="handleOpenFile"
          @navigate-to-zip-directory="handleNavigateToZipDirectory"
          @update:search-keyword="handleSearchKeywordUpdate"
          @show-message="handleShowMessage"
          @connection-changed="handleConnectionChanged"
        />

        <!-- 文件列表和编辑器区域 -->
        <div ref="workspaceRef" class="file-workspace">
        <!-- 文件列表面板 -->
        <FileListPanel
          :config="fileListPanelConfig"
          :width="panelWidth.left"
          :favorites="favoritePaths"
          :sort-by="sortBy"
          :sort-order="sortOrder"
          @file-click="handleFileClick"
          @file-double-click="handleFileDoubleClick"
          @toggle-favorite="handleToggleFavorite"
          @start-editing="handleStartEditing"
          @save-editing="handleSaveEditing"
          @cancel-editing="handleCancelEditing"
          @name-update="handleNameUpdate"
          @context-menu="handleContextMenu"
          @sort="setSort"
          ref="fileListPanelRef"
        />

        <!-- 分隔条 -->
        <div class="resizer" @mousedown="handleHorizontalResize"></div>

        <!-- 文件编辑器面板（始终显示，无选中文件时为空白预览区） -->
        <FileEditorPanel
          :config="fileEditorPanelConfig"
          :width="panelWidth.right"
          :current-directory="filePath"
          @save="handleSave"
          @reset="handleReset"
          @toggle-edit-mode="handleToggleEditMode"
          @start-resize="handleStartResize"
          @content-update="handleContentUpdate"
          @image-load="handleImageLoad"
          @image-error="handleImageError"
          @open-local-file="handleOpenLocalFile"
        />
      </div>
      </div>
    </div>

    <!-- 右键菜单 -->
    <ContextMenu
      :config="contextMenuConfig"
      @action="handleContextMenuAction"
      @close="hideContextMenu"
    />

    <!-- 输入对话框 -->
    <a-modal
      v-model:visible="inputDialogVisible"
      :title="inputDialogTitle"
      :ok-text="inputDialogOkText"
      :cancel-text="'取消'"
      @ok="handleInputDialogConfirm"
      @cancel="inputDialogVisible = false"
      :mask-closable="false"
    >
      <a-input
        v-model="inputDialogValue"
        :placeholder="inputDialogPlaceholder"
        @keyup.enter="handleInputDialogConfirm"
        ref="inputDialogInputRef"
        :max-length="255"
        show-word-limit
      />
    </a-modal>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
import { getPathSeparator } from '@/utils/fileUtils'
import { Message, Modal } from '@arco-design/web-vue'
import { marked, renderMermaidDiagrams, rerenderMermaidDiagrams, setCurrentFileDir, setFileServerBase } from '@/utils/markedExtensions'
import { useThemeStore } from '@/stores/theme'

// 导入子组件
import Toolbar from './components/Toolbar.vue'
import Sidebar from './components/Sidebar.vue'
import FileListPanel from './components/FileListPanel.vue'
import FileEditorPanel from './components/FileEditorPanel.vue'
import ContextMenu from './components/ContextMenu.vue'

// 导入 Composables
import { useFileOperations } from './composables/useFileOperations'
import { useFavorites } from './composables/useFavorites'
import { usePathNavigation } from './composables/usePathNavigation'
import { useFilePreview } from './composables/useFilePreview'
import { useFileEdit } from './composables/useFileEdit'
import { useCommonPaths } from './composables/useCommonPaths'

// 导入工具函数
import { getFileName, sortFileList } from '@/utils/fileUtils'
import { isImageFile, isVideoFile, isAudioFile, isPdfFile, isHtmlFile, isMarkdownFile, isExcelFile, isWordFile, isCsvFile } from '@/utils/fileTypeHelpers'
import { listDir, saveBase64File } from '@/api/system'
import { connectionManager } from '@/api/connection-manager'
import { STORAGE_KEYS, DEFAULTS, UI_TEXT, VALIDATION_RULES, FILE_EXTENSIONS, FILE_SIZE_THRESHOLDS } from '@/utils/constants'
import { createResizeHandler } from '@/utils/resize'

// 导入类型
import type { FileItem, FavoriteFile, ContextMenuConfig, ShortcutPath } from '@/types/file-system'

// 定义组件名称
defineOptions({
  name: 'FileSystem'
})

// ========== 工具函数（最先定义，避免初始化顺序问题） ==========

// 判断是否可以在编辑/预览模式之间切换（HTML/Markdown/CSV）
const isEditableWithPreview = (filename: string): boolean => {
  const ext = filename.split('.').pop()?.toLowerCase() || ''
  return ['html', 'htm', 'md', 'markdown', 'csv', 'tsv'].includes(ext)
}

// ========== 状态管理 ==========

// 文件列表相关
const fileList = ref<FileItem[]>([])
const fileLoading = ref(false)
const selectedFileItem = ref<FileItem | null>(null)
const toolbarRef = ref<InstanceType<typeof import('./components/Toolbar.vue').default> | null>(null)
const triggerConnectionDialog = ref(0)
const pendingEditProfileId = ref<string | null>(null)

function handleOpenConnectionDialog(editId?: string) {
  if (editId) {
    pendingEditProfileId.value = editId
    triggerConnectionDialog.value++
  } else {
    pendingEditProfileId.value = null
    triggerConnectionDialog.value++
  }
}

// 排序状态（带 localStorage 持久化）
const SORT_STORAGE_KEY = STORAGE_KEYS.FILESYSTEM.SORT
type SortField = 'name' | 'size' | 'type' | 'modified_time'
const defaultSort: { sortBy: SortField; sortOrder: 'asc' | 'desc' } = { sortBy: 'name', sortOrder: 'asc' }
let savedSort: typeof defaultSort | null = null
try { savedSort = JSON.parse(localStorage.getItem(SORT_STORAGE_KEY) || '') } catch { /* localStorage 不可用则使用默认排序 */ }
const sortBy = ref<SortField>(savedSort?.sortBy || defaultSort.sortBy)
const sortOrder = ref(savedSort?.sortOrder || defaultSort.sortOrder)

const doSort = () => {
  fileList.value = sortFileList(fileList.value, { sortBy: sortBy.value, sortOrder: sortOrder.value })
  localStorage.setItem(SORT_STORAGE_KEY, JSON.stringify({ sortBy: sortBy.value, sortOrder: sortOrder.value }))
}

const setSort = (field: SortField) => {
  if (sortBy.value === field) {
    sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc'
  } else {
    sortBy.value = field
    sortOrder.value = 'asc'
  }
  doSort()
}

// 导航锁：防止同时执行多个导航操作
const isNavigating = ref(false)

// 编辑相关
const editingFilePath = ref('')
const editingFileName = ref('')

// 侧边栏
const showSidebar = ref(true)

// 搜索
const searchKeyword = ref('')

// 过滤后的文件列表（基于搜索关键词）
const filteredFileList = computed(() => {
  const keyword = searchKeyword.value.trim().toLowerCase()
  if (!keyword) return fileList.value
  return fileList.value.filter(item =>
    item.name.toLowerCase().includes(keyword)
  )
})

// 面板宽度（带 localStorage 持久化）
const restorePanelWidth = (): { left: number; right: number } => {
  try {
    const saved = localStorage.getItem(STORAGE_KEYS.FILESYSTEM.PANEL_WIDTH)
    if (saved) {
      const parsed = JSON.parse(saved)
      if (parsed.left && parsed.right) {
        return parsed
      }
    }
  } catch (error) {
    console.error('恢复面板宽度失败:', error)
  }
  return {
    left: DEFAULTS.DEFAULT_PANEL_WIDTH,
    right: 100 - DEFAULTS.DEFAULT_PANEL_WIDTH
  }
}

const savePanelWidth = (width: { left: number; right: number }) => {
  try {
    localStorage.setItem(STORAGE_KEYS.FILESYSTEM.PANEL_WIDTH, JSON.stringify(width))
  } catch (error) {
    console.error('保存面板宽度失败:', error)
  }
}

const panelWidth = ref(restorePanelWidth())
const workspaceRef = ref<HTMLElement | null>(null)

// 系统路径（使用 composable）
const { commonPaths, systemPaths, loadCommonPaths } = useCommonPaths()

// 右键菜单
const contextMenuConfig = ref<ContextMenuConfig>({
  visible: false,
  x: 0,
  y: 0,
  context: 'blank',
  selectedFile: undefined
})

// 输入对话框
const inputDialogVisible = ref(false)
const inputDialogTitle = ref('')
const inputDialogPlaceholder = ref('')
const inputDialogOkText = ref('确定')
const inputDialogValue = ref('')
const inputDialogCallback = ref<((value: string) => void) | null>(null)
const inputDialogInputRef = ref()
const fileListPanelRef = ref()

// ========== Composables 初始化 ==========

// 文件操作
const fileOps = useFileOperations({
  onSuccess: (operation, data) => {
    // 操作成功处理
  },
  onError: (operation, error) => {
    // 注意：具体的操作（如重命名、删除等）已经有自己的错误处理
    // 这里只记录日志，避免重复显示错误消息
    console.error(`[FileOperation] ${operation} 失败:`, error)
  }
})

// 收藏夹
const { favorites, draggingState, toggleFavorite: toggleFav, removeFavorite: removeFav, isFavorite, togglePin, updateFavoritePath, onLongPressStart, onLongPressCancel, onDragStart, onDragOver, onDrop, onDragEnd } = useFavorites()

// 路径导航
const { filePath, history, navigate, back, forward, onPathSelect, onPathEnter, browseDirectory, getParentPath } =
  usePathNavigation({
    onListDirectory: async (path) => {
      await loadDirectory(path)
    }
  })

// 文件预览
const { previewUrl, updatePreviewUrl, imageLoading, currentImageDimensions, detectByContent } =
  useFilePreview({
    filePath
  })

// 文件编辑
const { fileContent, originalContent, isEditMode, fileContentHeight, contentChanged, canSaveFile, canResetContent, loadFile, saveFile, resetContent, clearContent, updateFilePath, toggleEditMode, updateContent, setEditorHeight, isBinaryFile: isBinaryFileRef, fileVersion } =
  useFileEdit({
    currentFilePath: selectedFileItem,
    currentDirectory: filePath
  })

// ========== 计算属性 ==========

const favoritePaths = computed(() => favorites.value.map(f => f.path))

const hasSelectedFile = computed(() => selectedFileItem.value !== null)

// 工具栏配置
const toolbarConfig = computed(() => ({
  filePath: filePath.value || '',
  pathHistory: history.value.paths.slice(-10),
  commonPaths: commonPaths.value,
  isBrowsingZip: false,
  displayPath: '',
  zipFileName: '',
  zipBreadcrumbs: [],
  fileLoading: fileLoading.value,
  showSidebar: showSidebar.value,
  sortBy: sortBy.value,
  sortOrder: sortOrder.value,
  searchKeyword: searchKeyword.value
}))

// 侧边栏配置
const sidebarConfig = computed(() => ({
  visible: showSidebar.value,
  favoriteFiles: favorites.value,
  draggingState: draggingState.value
}))

// 文件列表面板配置
const fileListPanelConfig = computed(() => ({
  fileList: filteredFileList.value,
  fileLoading: fileLoading.value,
  selectedFileItem: selectedFileItem.value,
  editingFilePath: editingFilePath.value,
  editingFileName: editingFileName.value
}))

// 计算渲染内容
const computeRendered = computed(() => {
  const currentFileName = selectedFileItem.value?.name || ''
  if (isHtmlFile(currentFileName)) {
    return fileContent.value || ''
  } else if (isMarkdownFile(currentFileName)) {
    // 使用配置好的 marked 渲染 Markdown（支持 mermaid + 图片相对路径转换）
    try {
      const content = fileContent.value || ''

      // 设置图片路径转换所需的上下文（renderer.image 钩子中读取）
      // dir: 当前 md 文件所在目录（从文件完整路径中去掉文件名）
      const fullPath = selectedFileItem.value?.path || ''
      const dir = fullPath ? fullPath.replace(/[/\\][^/\\]+$/, '') : (filePath.value || '')
      setCurrentFileDir(dir)

      // 设置文件服务器 Base URL
      const isRemote = connectionManager.isRemote()
      const base = isRemote
        ? (connectionManager.getFileServerBaseURL().replace(/\/$/, '') + '/api/v1/proxy/localfs')
        : 'http://localhost:2652/localfs'
      setFileServerBase(base)

      return marked.parse(content) as string
    } catch (error) {
      console.error('Markdown 解析失败:', error)
      return fileContent.value || ''
    }
  }
  return ''
})

// 文件编辑器面板配置
const fileEditorPanelConfig = computed(() => {
  const currentFileName = selectedFileItem.value?.name || ''
  const currentFileFullPath = selectedFileItem.value?.path || ''
  const currentFileExtension = currentFileName.split('.').pop()?.toLowerCase() || ''

  return {
    currentFileName,
    currentFileFullPath,
    previewUrl: previewUrl.value,
    fileContent: fileContent.value,
    rendered: computeRendered.value,
    isEditMode: isEditMode.value,
    fileContentHeight: fileContentHeight.value,
    isImageView: isImageFile(currentFileName),
    isVideoView: isVideoFile(currentFileName),
    isAudioView: isAudioFile(currentFileName),
    isPdfFile: isPdfFile(currentFileName),
    isHtmlFile: isHtmlFile(currentFileName),
    isMarkdownFile: isMarkdownFile(currentFileName),
    isExcelFile: isExcelFile(currentFileName),
    isWordFile: isWordFile(currentFileName),
    isCsvFile: isCsvFile(currentFileName),
    officeLoading: false,
    officeError: null,
    canSaveFile: canSaveFile.value,
    canResetContent: canResetContent.value,
    canPreviewFile: isEditableWithPreview(currentFileName),
    imageLoading: imageLoading.value,
    currentImageDimensions: currentImageDimensions.value,
    currentFileExtension,
    isBinaryFile: isBinaryFileRef.value,
    fileMtime: selectedFileItem.value?.modified_time || ''
  }
})

// ========== 事件处理 ==========

// 工具栏事件
const handleFilePathUpdate = (path: string) => {
  filePath.value = path
}

const handleSidebarToggle = (show: boolean) => {
  showSidebar.value = show
}

const handleRefresh = async () => {
  await loadDirectory(filePath.value)
}

// 连接切换后重置路径并刷新文件列表
connectionManager.onStateChange(async (state) => {
  if (state === 'connected') {
    await loadCommonPaths()
    const targetPath = connectionManager.isRemote() ? '/' : 'C:/'
    filePath.value = targetPath
    await loadDirectory(targetPath)
  }
})

const handleSearchKeywordUpdate = (keyword: string) => {
  searchKeyword.value = keyword
}

// 用户主动切换连接时重置到根路径
const handleConnectionChanged = async () => {
  await loadCommonPaths()
  await navigate(connectionManager.isRemote() ? '/' : 'C:/')
}

const handleGoToPath = async (path: string) => {
  await navigate(path)
}

const handleOpenFile = async (path: string) => {
  // 检查是文件还是目录
  try {
    const parentPath = getParentPath(path)
    const files = await listDir(parentPath)

    // 后端已统一返回 / 路径，直接比较
    const targetFile = files.find(f => f.path === path)

    if (targetFile) {
      if (targetFile.isDir) {
        // 是目录，导航进入
        await navigate(path)
      } else {
        // 是文件，先加载内容，再更新选中状态（避免闪烁）
        await loadFileContent(path)
        // 内容加载完成后再更新选中状态，确保 fileContent 和 selectedFileItem 同步
        selectedFileItem.value = targetFile
      }
    } else {
      // 未找到，尝试直接导航（可能是目录）
      await navigate(path)
    }
  } catch (error) {
    console.error('打开文件失败:', error)
    // 如果出错，尝试直接导航
    await navigate(path)
  }
}

// 处理 HTML/Markdown 预览中的本地文件链接点击
const handleOpenLocalFile = async (link: string) => {
  if (!link) return

  try {
    let targetPath = link

    // 剥离 /localfs/ 前缀（HTML 预览模式下 JS 生成的路径带此前缀）
    if (link.startsWith('/localfs/')) {
      targetPath = link.replace(/^\/localfs\//, '').replace(/\//g, '\\')
    }
    // 如果是相对路径，基于当前预览文件所在目录解析
    else if (!link.match(/^[A-Za-z]:/) && !link.startsWith('/')) {
      const currentFilePath = fileEditorPanelConfig.value.currentFileFullPath
      if (currentFilePath) {
        const currentDir = getParentPath(currentFilePath)
        targetPath = resolveRelativePath(currentDir, link)
      }
    }

    // 调用打开文件处理
    await handleOpenFile(targetPath)
  } catch (error) {
    console.error('打开本地文件链接失败:', error)
    Message.error(`无法打开文件: ${link}`)
  }
}

// 解析相对路径为绝对路径
const resolveRelativePath = (basePath: string, relativePath: string): string => {
  // 统一使用 / 作为分隔符
  const base = basePath.replace(/\\/g, '/')
  const relative = relativePath.replace(/\\/g, '/')

  // 处理 ./ 和 ../
  const parts = base.split('/')
  const segments = relative.split('/')

  for (const segment of segments) {
    if (segment === '..') {
      parts.pop()
    } else if (segment !== '.' && segment !== '') {
      parts.push(segment)
    }
  }

  return parts.join('/')
}

const handleNavigateToZipDirectory = async (path: string) => {
  // 暂时不处理 ZIP
}

const handleExitZip = () => {
  // 暂时不处理 ZIP
}

const handleShowMessage = (message: string, type: 'success' | 'error' | 'warning' | 'info') => {
  Message[type](message)
}

// 侧边栏事件
const handleOpenFavorite = async (file: FavoriteFile) => {
  // 根据路径格式自动切换连接（Linux 路径 → 远程，Windows 路径 → 本地）
  const isLinuxPath = /^[\/]/.test(file.path) && !/^[A-Za-z]:/.test(file.path)
  const shouldBeRemote = isLinuxPath
  const isCurrentlyRemote = connectionManager.isRemote()

  if (shouldBeRemote !== isCurrentlyRemote) {
    // 需要切换连接
    if (shouldBeRemote) {
      // 切换到远程：找第一个 remote profile
      const remoteProfile = connectionManager.profiles.find(p => p.type === 'remote')
      if (remoteProfile) {
        connectionManager.connect(remoteProfile.id)
      }
    } else {
      // 切换到本地
      connectionManager.disconnect()
    }
    await loadCommonPaths()
  }

  if (file.isDir) {
    await navigate(file.path)
  } else {
    await selectFile(file.path)
  }
}

const handleRemoveFavorite = (path: string) => {
  removeFav(path)
}

const handleTogglePin = (path: string) => {
  togglePin(path)
}

const handleLongPressStart = (event: MouseEvent | TouchEvent, index: number) => {
  onLongPressStart(event, index)
}

const handleLongPressCancel = () => {
  onLongPressCancel()
}

const handleDragStart = (event: DragEvent, index: number) => {
  onDragStart(event, index)
}

const handleDragOver = (event: DragEvent) => {
  onDragOver(event)
}

const handleDrop = (event: DragEvent, targetIndex: number) => {
  onDrop(event, targetIndex)
}

const handleDragEnd = () => {
  onDragEnd()
}

// 文件列表事件
const handleFileClick = async (file: FileItem) => {
  // 正常文件系统浏览
  if (file.isDir) {
    // 目录：使用 navigate 函数，确保历史记录正确更新
    await navigate(file.path)
  } else {
    // 文件：选中并读取内容
    selectedFileItem.value = file
    loadFileContent(file.path)
  }
}

const handleFileDoubleClick = async (file: FileItem) => {
  if (file.isDir) {
    await navigate(file.path)
  } else {
    selectFile(file.path)
  }
}

const handleToggleFavorite = (file: FileItem) => {
  toggleFav(file)
}


const handleStartEditing = (path: string, name: string) => {
  editingFilePath.value = path
  editingFileName.value = name

  // 自动聚焦到输入框并选中文件名（不包括扩展名）
  nextTick(() => {
    fileListPanelRef.value?.focusEditingItem()
  })
}

const handleNameUpdate = (newName: string) => {
  editingFileName.value = newName
}

const handleSaveEditing = async (oldPath: string, newName: string) => {
  if (!newName || newName.trim() === '') {
    Message.warning('文件名不能为空')
    return
  }

  const trimmedName = newName.trim()

  // 如果名称没有变化，直接返回
  const lastSep = Math.max(oldPath.lastIndexOf('\\'), oldPath.lastIndexOf('/'))
  const oldName = oldPath.substring(lastSep + 1)
  if (trimmedName === oldName) {
    editingFilePath.value = ''
    editingFileName.value = ''
    return
  }

  // 验证文件名
  const invalidChars = /[<>:"/\\|?*]/g
  if (invalidChars.test(trimmedName)) {
    Message.error('文件名包含非法字符：<>:"/\\|?*')
    return
  }

  // 构造新路径
  const separator = getPathSeparator(oldPath)
  const dirPath = oldPath.substring(0, oldPath.lastIndexOf(separator))
  const newPath = dirPath + separator + trimmedName

  // 检查是否正在编辑该文件且有未保存的修改
  if (selectedFileItem.value?.path === oldPath && contentChanged.value) {
    const confirmed = await new Promise<boolean>((resolve) => {
      Modal.confirm({
        title: '有未保存的修改',
        content: '该文件有未保存的修改，是否先保存再重命名？',
        okText: '保存并重命名',
        cancelText: '直接重命名（丢失修改）',
        onOk: async () => {
          try {
            await saveFile(oldPath)
            resolve(true)
          } catch (error) {
            Message.error('保存失败，已取消重命名')
            resolve(false)
          }
        },
        onCancel: () => {
          resolve(false)
        }
      })
    })

    if (!confirmed) {
      // 用户取消或保存失败，不执行重命名
      editingFilePath.value = ''
      editingFileName.value = ''
      return
    }
  }

  try {
    // 标记是否需要重命名后仅更新路径（内容不变，零闪烁）
    let needUpdatePath = false

    // 如果重命名的是当前打开的文件
    if (selectedFileItem.value?.path === oldPath && !selectedFileItem.value.isDir) {
      needUpdatePath = true
    }

    const renamedFile = await fileOps.rename(oldPath, trimmedName)

    // 更新文件列表（保留收藏状态）
    updateFileInList(oldPath, renamedFile)

    // 如果重命名的是收藏的文件，更新收藏夹中的路径（保留置顶状态）
    if (isFavorite(oldPath)) {
      updateFavoritePath(oldPath, trimmedName)
    }

    Message.success(`✓ 重命名成功: ${trimmedName}`)

    // 仅更新路径关联，不重新加载内容（编辑器内容不变，零闪烁）
    if (needUpdatePath && !renamedFile.isDir) {
      selectedFileItem.value = renamedFile
      updateFilePath(newPath)
      updatePreviewUrl(newPath)
    }
  } catch (error: any) {
    // 提取错误信息
    let errorMsg = error?.message || error?.toString() || '未知错误'

    // 清理后端返回的错误前缀
    errorMsg = errorMsg
      .replace(/^rename\s+.*?:\s*/i, '')
      .replace(/^create\s+.*?:\s*/i, '')
      .replace(/^delete\s+.*?:\s*/i, '')
      .trim()

    // 针对常见错误提供友好提示
    if (errorMsg.includes('being used by another process') ||
        errorMsg.includes('被另一个进程占用')) {
      errorMsg = '文件正在被其他程序使用，请先关闭该文件后重试'
      if (selectedFileItem.value?.isDir) {
        errorMsg = '文件夹正在被其他程序使用（如文件管理器、终端等），请先关闭后重试'
      }
    } else if (errorMsg.includes('access is denied') ||
               errorMsg.includes('permission denied')) {
      errorMsg = '权限不足，无法重命名该文件'
    } else if (errorMsg.includes('no such file') ||
               errorMsg.includes('cannot find')) {
      errorMsg = '文件不存在，可能已被删除或移动'
    }

    Message.error(`重命名失败: ${errorMsg}`)

    // 失败时恢复编辑状态
    editingFilePath.value = oldPath
    editingFileName.value = oldName
    return
  } finally {
    // 清空编辑状态
    editingFilePath.value = ''
    editingFileName.value = ''
  }
}

const handleCancelEditing = () => {
  editingFilePath.value = ''
  editingFileName.value = ''
}

const handleContextMenu = (event: MouseEvent, file: FileItem | null = null) => {
  event.preventDefault()
  event.stopPropagation()

  contextMenuConfig.value = {
    visible: true,
    x: event.clientX,
    y: event.clientY,
    context: file ? 'file' : 'blank',
    selectedFile: file || undefined
  }
}

const hideContextMenu = () => {
  contextMenuConfig.value.visible = false
}

const handleContextMenuAction = async (action: string, payload?: any) => {
  switch (action) {
    case 'createFile':
      await handleCreateFile()
      break
    case 'createDir':
      await handleCreateDir()
      break
    case 'openWithSystem':
      Message.info('系统打开功能开发中...')
      break
    case 'rename':
      if (payload) {
        handleStartEditing(payload.path, payload.name)
      }
      break
    case 'delete':
      if (payload) {
        await handleDeleteFile(payload)
      }
      break
  }
  hideContextMenu()
}

// ========== 创建文件/文件夹 ==========

/**
 * 显示输入对话框
 */
const showInputDialog = (
  title: string,
  placeholder: string,
  callback: (value: string) => void,
  okText: string = '确定'
) => {
  // 隐藏右键菜单
  hideContextMenu()

  inputDialogTitle.value = title
  inputDialogPlaceholder.value = placeholder
  inputDialogOkText.value = okText
  inputDialogValue.value = ''
  inputDialogCallback.value = callback
  inputDialogVisible.value = true

  // 自动聚焦到输入框
  setTimeout(() => {
    inputDialogInputRef.value?.focus()
  }, 100)
}

/**
 * 处理输入对话框确认
 */
const handleInputDialogConfirm = () => {
  const value = inputDialogValue.value?.trim()

  if (!value) {
    Message.warning('请输入内容')
    return
  }

  // 调用回调函数
  if (inputDialogCallback.value) {
    inputDialogCallback.value(value)
  }

  // 关闭对话框
  inputDialogVisible.value = false
}

/**
 * 创建文件处理
 */
const handleCreateFile = async () => {
  if (!filePath.value) {
    Message.error('请先选择一个目录')
    return
  }

  showInputDialog(
    UI_TEXT.CREATE_FILE,
    UI_TEXT.ENTER_FILE_NAME,
    async (fileName: string) => {
      // 验证文件名
      if (!validateFileName(fileName)) {
        Message.error(UI_TEXT.FILE_NAME_INVALID)
        // 重新显示对话框
        setTimeout(() => {
          inputDialogVisible.value = true
          inputDialogValue.value = fileName
        }, 100)
        return
      }

      // 检查是否已存在同名文件
      const existingFile = fileList.value.find(f => f.name === fileName && !f.isDir)
      if (existingFile) {
        Message.error(`文件 "${fileName}" 已存在`)
        // 重新显示对话框
        setTimeout(() => {
          inputDialogVisible.value = true
          inputDialogValue.value = fileName
        }, 100)
        return
      }

      try {
        const newFile = await fileOps.createNewFile(filePath.value, fileName)
        Message.success(`✓ 文件 "${fileName}" 创建成功`)
        addFileToList(newFile)
      } catch (error: any) {
        Message.error(`创建文件失败: ${error.message || error}`)
      }
    },
    '创建'
  )
}

/**
 * 创建文件夹处理
 */
const handleCreateDir = async () => {
  if (!filePath.value) {
    Message.error('请先选择一个目录')
    return
  }

  showInputDialog(
    UI_TEXT.CREATE_FOLDER,
    UI_TEXT.ENTER_FOLDER_NAME,
    async (folderName: string) => {
      // 验证文件夹名称
      if (!validateFileName(folderName)) {
        Message.error(UI_TEXT.FOLDER_NAME_INVALID)
        // 重新显示对话框
        setTimeout(() => {
          inputDialogVisible.value = true
          inputDialogValue.value = folderName
        }, 100)
        return
      }

      // 检查是否已存在同名文件夹
      const existingFolder = fileList.value.find(f => f.name === folderName && f.isDir)
      if (existingFolder) {
        Message.error(`文件夹 "${folderName}" 已存在`)
        // 重新显示对话框
        setTimeout(() => {
          inputDialogVisible.value = true
          inputDialogValue.value = folderName
        }, 100)
        return
      }

      try {
        const newDir = await fileOps.createNewDir(filePath.value, folderName)
        Message.success(`✓ 文件夹 "${folderName}" 创建成功`)
        addFileToList(newDir)
      } catch (error: any) {
        Message.error(`创建文件夹失败: ${error.message || error}`)
      }
    },
    '创建'
  )
}

/**
 * 验证文件名是否合法
 */
const validateFileName = (name: string): boolean => {
  return !VALIDATION_RULES.ILLEGAL_FILE_NAME_CHARS.test(name) && name.trim().length > 0
}

/**
 * 删除文件或目录
 */
const handleDeleteFile = async (file: FileItem) => {
  const targetPath = file.path
  const isDirectory = file.isDir
  const fileName = file.name || targetPath

  // 根据类型显示不同的确认信息
  const confirmMessage = isDirectory
    ? `⚠️ 确定要删除整个目录吗？\n\n${fileName}\n\n此操作将删除目录及其所有内容，不可恢复！`
    : `确定要删除文件吗？\n\n${fileName}\n\n此操作不可恢复！`

  Modal.confirm({
    title: isDirectory ? '⚠️ 危险操作：删除目录' : '确认删除',
    content: confirmMessage,
    okText: '确定删除',
    cancelText: '取消',
    onOk: async () => {
      try {
        await fileOps.deletePath(targetPath)
        Message.success('删除成功')

        // 从文件列表中移除
        removeFileFromList(targetPath)

        // 如果删除的是收藏的文件，从收藏夹中移除
        if (isFavorite(targetPath)) {
          removeFav(targetPath)
        }

        // 如果删除的是当前选中的文件，清空编辑器
        if (selectedFileItem.value?.path === targetPath) {
          selectedFileItem.value = null
        }
      } catch (error: any) {
        Message.error(`删除失败: ${error.message || error}`)
      }
    }
  })
}

// 编辑器事件
const handleSave = async () => {
  if (selectedFileItem.value) {
    await saveFile(selectedFileItem.value.path)
  }
}

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

const handleReset = () => {
  resetContent()
}

const handleStartResize = createResizeHandler(
  () => workspaceRef.value,
  () => fileContentHeight.value,
  {
    direction: 'vertical',
    outputMode: 'pixels',
    minPixels: 200,
    onResize: (px) => { fileContentHeight.value = px },
  }
)

const handleContentUpdate = (content: string) => {
  // useFileEdit 内部会检查版本号和时间，防止过期更新
  updateContent(content, fileVersion.value)
}

const handleImageLoad = (dimensions: string) => {
  // 图片加载完成，更新尺寸
  currentImageDimensions.value = dimensions
}

const handleImageError = () => {
  // 图片加载错误
  currentImageDimensions.value = ''
}

// ========== 工具函数 ==========

// 判断是否为媒体文件（用于媒体预览）
const isMediaPreviewable = (filename: string): boolean => {
  return isImageFile(filename) ||
         isVideoFile(filename) ||
         isAudioFile(filename) ||
         isPdfFile(filename)
}

const selectFile = async (path: string) => {
  // 后端已统一返回 / 路径，直接比较
  const normalizedPath = path.toLowerCase()

  // 尝试在当前文件列表中查找
  const file = fileList.value.find(f => {
    const normalizedFilePath = f.path.toLowerCase()
    return normalizedFilePath === normalizedPath
  })

  if (file) {
    // 在当前列表中找到的文件
    selectedFileItem.value = file
  } else {
    // 不在当前列表中的文件（如收藏夹中的其他目录文件）
    // 构造一个基本的 FileItem 对象
    const fileName = getFileName(path)
    selectedFileItem.value = {
      path,
      name: fileName,
      isDir: false,
      size: 0,
      modified_time: '',
      is_favorite: isFavorite(path)
    }
  }

  // 加载文件内容
  await loadFileContent(path)

  // 记住上次打开的文件
  localStorage.setItem(STORAGE_KEYS.FILESYSTEM.LAST_OPENED_FILE, path)
}

const loadFileContent = async (path: string) => {
  try {
    updatePreviewUrl(path)

    const fileName = getFileName(path)
    const fileItem = selectedFileItem.value
    const fileSize = fileItem?.size || 0

    // 首先检查扩展名是否可识别为媒体文件
    if (isMediaPreviewable(fileName)) {
      // 媒体文件：清空文本内容，避免显示之前文件的内容
      clearContent()
      return
    }

    // 大文件预检：基于目录列表中的 size 字段，避免读取大文件导致卡死
    if (fileSize > FILE_SIZE_THRESHOLDS.BIG_FILE) {
      const sizeMB = (fileSize / 1024 / 1024).toFixed(2)
      clearContent()
      fileContent.value = `================================================================
⚠️  文件过大 (${sizeMB} MB)
================================================================

文件名:    ${fileName}
完整路径:  ${path}
文件大小:  ${sizeMB} MB

================================================================
当前文件超过 ${(FILE_SIZE_THRESHOLDS.BIG_FILE / 1024)}KB，不适合在编辑器中打开。

💡 提示:
   • 右键菜单 → "使用系统程序打开" 在默认应用中打开
   • 右键菜单 → "在资源管理器中显示" 查看文件位置
================================================================`
      return
    }

    // 对于小文件（≤500KB）且扩展名不可识别的情况，进行内容检测
    if (fileSize > 0 && fileSize <= 500 * 1024) {
      const ext = fileName.split('.').pop()?.toLowerCase() || ''
      // 如果没有扩展名，或者扩展名不在已知列表中
      if (!ext || !FILE_EXTENSIONS.IMAGE.includes(ext)) {
        try {
          const detected = await detectByContent(path, fileSize)
          if (detected && detected.category === 'image') {
            // 内容检测确认为图片，清空文本内容，让预览URL生效
            clearContent()
            return
          }
        } catch {
          // 内容检测失败，继续按普通文件处理
        }
      }
    }

    // 其他情况：加载为文本文件
    await loadFile(path)
  } catch (error) {
    Message.error(`读取文件失败: ${error}`)
  }
}

const loadDirectory = async (path: string) => {
  fileLoading.value = true
  try {
    fileList.value = await fileOps.listDirectory(path)
    doSort()
  } catch (error) {
    Message.error(`加载目录失败: ${error}`)
  } finally {
    fileLoading.value = false
  }
}

/**
 * 添加文件到列表（保持排序）
 */
const addFileToList = (item: FileItem) => {
  fileList.value = [...fileList.value, { ...item, is_favorite: false }]
  doSort()
}

/**
 * 从列表中移除文件
 */
const removeFileFromList = (path: string) => {
  fileList.value = fileList.value.filter(f => f.path !== path)
}

/**
 * 更新列表中的文件信息（保留运行时状态如 is_favorite）
 */
const updateFileInList = (oldPath: string, newItem: FileItem) => {
  const index = fileList.value.findIndex(f => f.path === oldPath)
  if (index !== -1) {
    // 保留原有属性（如 is_favorite），更新其他字段
    fileList.value[index] = {
      ...fileList.value[index],  // 保留原有所有属性
      ...newItem,                 // 覆盖新字段
      is_favorite: fileList.value[index].is_favorite  // 确保保留收藏状态
    }
  }
}

// 加载 ZIP 目录内容
const loadZipDirectoryContents = async (zipPath: string, currentDir: string): Promise<FileItem[]> => {
  fileLoading.value = true
  try {
    const allFiles = await fileOps.listZipContents(zipPath)

    if (!allFiles || !Array.isArray(allFiles)) {
      throw new Error('ZIP 内容格式无效')
    }

    // 过滤显示当前目录的内容
    let filtered = allFiles
    if (currentDir) {
      filtered = allFiles.filter((f: any) => {
        const fileDir = f.path.substring(0, f.path.lastIndexOf('/')) || ''
        return fileDir === currentDir
      })
    } else {
      // 根目录：只显示顶层文件/文件夹
      filtered = allFiles.filter((f: any) => !f.path.includes('/'))
    }

    const result = filtered.map((f: any) => ({
      name: f.name,
      path: f.path,
      isDir: f.isDir,
      size: f.size || 0,
      modified_time: f.modified_time || '',
      is_favorite: false
    }))

    return sortFileList(result, { sortBy: sortBy.value, sortOrder: sortOrder.value })
  } catch (error) {
    console.error('加载 ZIP 目录失败:', error)
    Message.error(`加载 ZIP 目录失败: ${error}`)
    return []
  } finally {
    fileLoading.value = false
  }
}

// 提取 ZIP 中的图片文件并预览
const extractZipImageAndPreview = async (zipPath: string, filePath: string): Promise<void> => {
  fileLoading.value = true
  try {
    const temp = await fileOps.extractZipFileToTemp(zipPath, filePath)
    const url = await fileOps.getFileServerURL()
    const normalized = temp.replace(/\\/g, '/')
    updatePreviewUrl(normalized)
  } catch (error) {
    console.error('提取图片失败:', error)
    Message.error(`提取图片失败: ${error}`)
  } finally {
    fileLoading.value = false
  }
}

// 提取 ZIP 中的文本文件并读取
const extractZipTextAndRead = async (zipPath: string, filePath: string): Promise<void> => {
  fileLoading.value = true
  try {
    await fileOps.extractZipFile(zipPath, filePath)
    await loadFile(filePath)
  } catch (error) {
    console.error('提取文本文件失败:', error)
    Message.error(`提取文件失败: ${error}`)
  } finally {
    fileLoading.value = false
  }
}

const handleHorizontalResize = createResizeHandler(
  () => workspaceRef.value,
  () => panelWidth.value.left,
  {
    direction: 'horizontal',
    minPercent: DEFAULTS.MIN_PANEL_WIDTH,
    maxPercent: 100 - DEFAULTS.MIN_PANEL_WIDTH,
    onResize: (percent) => {
      panelWidth.value = { left: percent, right: 100 - percent }
    },
    onResizeEnd: () => savePanelWidth(panelWidth.value),
  }
)

// ========== 生命周期 ==========

onMounted(async () => {
  // 加载系统路径（阻塞，确保快捷入口就绪）
  await loadCommonPaths()

  // SFTP 连接是异步的，未就绪时跳过初始加载，由 onStateChange('connected') 触发
  if (connectionManager.isSftp() && connectionManager.state !== 'connected') {
    filePath.value = '/'
    return
  }

  // 初始化加载：远程模式强制用根路径，避免 localStorage 残留 Windows 路径
  const startPath = connectionManager.isRemote() ? '/'
    : (commonPaths.value.length > 0 ? commonPaths.value[0].path : 'C:/')
  if (filePath.value && !connectionManager.isRemote()) {
    await loadDirectory(filePath.value)
  } else {
    filePath.value = startPath
    await loadDirectory(startPath)
  }

  // 恢复上次打开的文件
  const lastFile = localStorage.getItem(STORAGE_KEYS.FILESYSTEM.LAST_OPENED_FILE)
  if (lastFile) {
    const normalized = lastFile.replace(/\\/g, '/').replace(/\/+$/, '')
    const currentDir = filePath.value.replace(/\\/g, '/').replace(/\/+$/, '')
    const lastFileDir = normalized.substring(0, normalized.lastIndexOf('/')) || '/'
    if (lastFileDir.toLowerCase() === currentDir.toLowerCase()) {
      const found = fileList.value.find(f => f.path.replace(/\\/g, '/').toLowerCase() === normalized.toLowerCase())
      if (found && !found.isDir) {
        await selectFile(found.path)
      }
    }
  }

  // 添加键盘快捷键
  window.addEventListener('keydown', handleKeyDown)
  window.addEventListener('click', hideContextMenu)

  // 添加粘贴事件监听（剪贴板图片）
  window.addEventListener('paste', handlePaste)
})

onUnmounted(() => {
  window.removeEventListener('keydown', handleKeyDown)
  window.removeEventListener('click', hideContextMenu)
  window.removeEventListener('paste', handlePaste)
})

// 键盘快捷键
const handleKeyDown = async (event: KeyboardEvent) => {
  // 防止导航过程中的并发操作
  if (isNavigating.value && ['ArrowLeft', 'ArrowRight', 'F5'].includes(event.key)) {
    event.preventDefault()
    return
  }

  // F5 刷新文件列表 + 重载当前预览文件
  if (event.key === 'F5') {
    event.preventDefault()
    if (filePath.value) {
      await loadDirectory(filePath.value)
      // 如果有正在预览的文件，同时重新加载其内容（类似重新点击一次）
      if (selectedFileItem.value && !selectedFileItem.value.isDir) {
        await loadFileContent(selectedFileItem.value.path)
      }
    }
    return
  }

  // Ctrl+H 打开历史记录面板
  if ((event.ctrlKey || event.metaKey) && event.key === 'h') {
    event.preventDefault()
    toolbarRef.value?.toggleHistoryDropdown?.()
    return
  }

  // Ctrl+Shift+C/D/E/F/G/H 快速打开对应盘符
  if ((event.ctrlKey || event.metaKey) && event.shiftKey) {
    const driveLetter = event.key.toUpperCase()
    if (['C', 'D', 'E', 'F', 'G', 'H'].includes(driveLetter)) {
      event.preventDefault()
      const drivePath = `${driveLetter}:\\`
      isNavigating.value = true
      try {
        await navigate(drivePath)
      } finally {
        isNavigating.value = false
      }
      return
    }

    // Ctrl+Shift+V 切换预览/编辑模式
    if (driveLetter === 'V') {
      event.preventDefault()
      // 检查是否有选中的文件
      if (!selectedFileItem.value) {
        Message.info('请先选择一个文件')
        return
      }

      // 检查文件是否支持预览/编辑切换
      const fileName = selectedFileItem.value.name
      if (!isEditableWithPreview(fileName)) {
        Message.info('当前文件类型不支持预览模式')
        return
      }

      // 切换编辑模式
      handleToggleEditMode()
      return
    }
  }

  // Ctrl+S 保存
  if ((event.ctrlKey || event.metaKey) && event.key === 's') {
    event.preventDefault()
    handleSave()
    return
  }

  // Ctrl+Z 重置内容
  if ((event.ctrlKey || event.metaKey) && event.key === 'z') {
    event.preventDefault()
    handleReset()
    return
  }

  // Ctrl+B 切换侧边栏
  if ((event.ctrlKey || event.metaKey) && event.key === 'b') {
    event.preventDefault()
    showSidebar.value = !showSidebar.value
    return
  }

  // Ctrl+Shift+N 新建文件夹（必须在 Ctrl+N 之前判断）
  if ((event.ctrlKey || event.metaKey) && event.shiftKey && event.key === 'n') {
    event.preventDefault()
    handleCreateDir()
    return
  }

  // Ctrl+N 新建文件
  if ((event.ctrlKey || event.metaKey) && event.key === 'n') {
    event.preventDefault()
    handleCreateFile()
    return
  }

  // Alt+← 后退到上一个目录
  if (event.altKey && event.key === 'ArrowLeft') {
    event.preventDefault()
    isNavigating.value = true
    try {
      await back()
    } finally {
      isNavigating.value = false
    }
    return
  }

  // Alt+→ 前进到下一个目录
  if (event.altKey && event.key === 'ArrowRight') {
    event.preventDefault()
    isNavigating.value = true
    try {
      await forward()
    } finally {
      isNavigating.value = false
    }
    return
  }

  // F2 重命名选中的文件
  if (event.key === 'F2' && selectedFileItem.value) {
    event.preventDefault()
    handleStartEditing(selectedFileItem.value.path, selectedFileItem.value.name)
    return
  }

  // Delete 删除选中的文件
  if (event.key === 'Delete' && selectedFileItem.value) {
    event.preventDefault()
    handleContextMenuAction('delete', selectedFileItem.value)
    return
  }
}

// 粘贴剪贴板图片到当前目录
const handlePaste = async (event: ClipboardEvent) => {
  // 忽略输入框内的粘贴
  const target = event.target as HTMLElement
  if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) return

  const items = event.clipboardData?.items
  if (!items) return

  const imageItem = Array.from(items).find(item => item.type.startsWith('image/'))
  if (imageItem) {
    event.preventDefault()
    await pasteImageToFile(imageItem)
  }
}

// 将剪贴板图片保存为文件
const pasteImageToFile = async (item: DataTransferItem) => {
  if (!filePath.value) {
    Message.warning('请先选择目标目录')
    return
  }

  try {
    const file = item.getAsFile()
    if (!file) {
      Message.error('无法获取剪贴板图片')
      return
    }

    // 生成文件名：clipboard_YYYYMMDD_HHmmss.ext
    const now = new Date()
    const pad = (n: number) => String(n).padStart(2, '0')
    const ts = `${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`
    const ext = file.type.split('/')[1] || 'png'
    const fileName = `clipboard_${ts}.${ext}`
    const separator = getPathSeparator(filePath.value)
    const fullPath = filePath.value + separator + fileName

    // 转换为 base64 并保存
    const reader = new FileReader()
    reader.onload = async () => {
      const result = reader.result as string
      // 移除 data:image/xxx;base64, 前缀，只保留纯 base64 内容
      const parts = result.split(',')
      const base64Data = parts.length > 1 ? parts[1] : ''
      if (!base64Data) {
        Message.error('图片数据格式无效')
        return
      }

      Message.loading({ content: `正在保存 ${fileName}...`, duration: 0 })
      try {
        await saveBase64File(fullPath, base64Data)
        Message.clear()
        Message.success(`已保存: ${fileName}`)
        loadDirectory(filePath.value)
      } catch (err: any) {
        Message.clear()
        Message.error('保存失败: ' + (err?.message || err))
      }
    }
    reader.readAsDataURL(file)
  } catch (err: any) {
    Message.error('粘贴失败: ' + (err?.message || err))
  }
}

// 渲染 Mermaid 图表
watch(async () => {
  if (isMarkdownFile(selectedFileItem.value?.name || '') && computeRendered.value) {
    await nextTick()
    await nextTick()
    await renderMermaidDiagrams()
    // 强制容器重新计算高度
    await nextTick()
  }
}, { immediate: true })

// 主题变化时重新渲染 mermaid 图表（跟随暗色/亮色）
const themeStore = useThemeStore()
watch(() => themeStore.isDark, async () => {
  if (isMarkdownFile(selectedFileItem.value?.name || '') && computeRendered.value) {
    try {
      // 等 DOM 更新完成后再重新渲染，确保 isDarkTheme() 能读到正确的主题属性
      await nextTick()
      await rerenderMermaidDiagrams()
    } catch { /* 忽略 */ }
  }
})
</script>

<style scoped>
.file-system-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.main-content {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.workspace-area {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-width: 0;
}

.file-workspace {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.resizer {
  width: 3px;
  background: var(--color-border);
  cursor: col-resize;
  transition: background 0.2s;
  flex-shrink: 0;
}

.resizer:hover {
  background: var(--color-primary-light-1);
}
</style>
