import { nextTick, ref, type Ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useSnackbar } from '@/composables/useSnackbar'
import { useFilesStore } from '@/stores/files'
import type { FileEntry } from '@/types/api'
import { isPermissionDeniedMessage } from '@/utils/fileMode'
import {
  exceedsEditorMaxSize,
  FILE_EDITOR_MAX_BYTES,
  FILE_PRELOAD_MAX_BYTES,
  invalidateCachedFile,
  readRemoteFileContent,
} from '@/utils/filePreload'
import { joinRemotePath } from '@/utils/remotePath'

function formatSize(bytes: number | undefined) {
  if (!bytes) return '—'
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
  return `${(bytes / 1024 / 1024).toFixed(1)} MB`
}

export interface OpenFilePayload {
  path: string
  name: string
  content: string
  eol: 'LF' | 'CRLF'
  etag: string | null
  mtime: number | null
}

export function useFilesEditor(options: {
  browse: (opts?: { force?: boolean }) => Promise<void | undefined>
  invalidateAfterMutation: (parentPath?: string) => void
  actionLoading: Ref<boolean>
}) {
  const snackbar = useSnackbar()
  const store = useFilesStore()
  const { selectedServer, currentPath, lastReadDeniedPath, accessHintDismissed } =
    storeToRefs(store)

  const showPreview = ref(false)
  const previewTitle = ref('')
  const previewContent = ref('')
  const previewLoading = ref(false)

  const editorWorkbench = ref<{
    openFile: (p: OpenFilePayload) => void
  } | null>(null)

  async function waitForEditorWorkbench(): Promise<{
    openFile: (p: OpenFilePayload) => void
  } | null> {
    const deadline = Date.now() + 8000
    while (Date.now() < deadline) {
      if (editorWorkbench.value?.openFile) return editorWorkbench.value
      await new Promise((r) => setTimeout(r, 40))
      await nextTick()
    }
    return editorWorkbench.value
  }

  function rejectOversizedFile(item: FileEntry, action: '编辑' | '查看'): boolean {
    if (exceedsEditorMaxSize(item.size)) {
      snackbar(
        `文件超过 2MB（${formatSize(item.size)}），无法${action}，请使用终端`,
        'warning',
      )
      return true
    }
    return false
  }

  function onEditorSaved(path: string) {
    if (selectedServer.value && path) {
      invalidateCachedFile(selectedServer.value, path)
    }
    options.invalidateAfterMutation()
    void options.browse({ force: true })
  }

  async function previewFile(item: FileEntry) {
    if (!selectedServer.value) return
    if (rejectOversizedFile(item, '查看')) return
    const remotePath = joinRemotePath(currentPath.value, item.name)
    previewTitle.value = item.name
    previewContent.value = ''
    previewLoading.value = true
    showPreview.value = true
    if ((item.size ?? 0) > FILE_PRELOAD_MAX_BYTES) {
      snackbar(`文件较大（${formatSize(item.size)}），正在加载…`, 'info')
    }
    try {
      const result = await readRemoteFileContent(
        selectedServer.value,
        remotePath,
        FILE_EDITOR_MAX_BYTES,
      )
      previewContent.value = result.content || '(空文件)'
    } catch (e: unknown) {
      const msg = e instanceof Error ? e.message : '读取失败'
      previewContent.value = msg
      if (isPermissionDeniedMessage(msg)) {
        lastReadDeniedPath.value = item.name
        accessHintDismissed.value = false
      }
      snackbar(msg, 'error')
    } finally {
      previewLoading.value = false
    }
  }

  async function editFile(item: FileEntry) {
    if (!selectedServer.value) return
    if (rejectOversizedFile(item, '编辑')) return
    if ((item.size ?? 0) > FILE_PRELOAD_MAX_BYTES) {
      snackbar(`文件较大（${formatSize(item.size)}），正在加载…`, 'info')
    }
    options.actionLoading.value = true
    try {
      const path = joinRemotePath(currentPath.value, item.name)
      const result = await readRemoteFileContent(
        selectedServer.value,
        path,
        FILE_EDITOR_MAX_BYTES,
      )
      const wb = await waitForEditorWorkbench()
      if (!wb) {
        snackbar('编辑器尚未就绪，请稍后重试', 'error')
        return
      }
      wb.openFile({
        path,
        name: item.name,
        content: result.content,
        eol: result.eol,
        etag: result.etag,
        mtime: result.mtime,
      })
    } catch (e: unknown) {
      const msg = e instanceof Error ? e.message : '读取失败'
      if (isPermissionDeniedMessage(msg)) {
        lastReadDeniedPath.value = item.name
        accessHintDismissed.value = false
      }
      snackbar(msg, 'error')
    } finally {
      options.actionLoading.value = false
    }
  }

  return {
    showPreview,
    previewTitle,
    previewContent,
    previewLoading,
    editorWorkbench,
    waitForEditorWorkbench,
    previewFile,
    editFile,
    onEditorSaved,
    formatSize,
  }
}
