import axios, { type AxiosError } from 'axios'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { documentApi, type Document, type DocumentChunk } from '@/api/document'
import { folderApi, type FolderTree } from '@/api/folder'

const KNOWLEDGE_FOLDER_STORAGE_KEY = 'knowledge.currentFolderId'

function getPersistedKnowledgeFolderId() {
  return typeof window !== 'undefined' ? localStorage.getItem(KNOWLEDGE_FOLDER_STORAGE_KEY) : null
}

function normalizeFolderId(value: unknown) {
  return typeof value === 'string' && value ? value : null
}

export function useKnowledgeView() {
  const route = useRoute()
  const router = useRouter()
  const initialFolderId = normalizeFolderId(route.query.folder_id) ?? getPersistedKnowledgeFolderId()
  const folders = ref<FolderTree[]>([])
  const documents = ref<Document[]>([])
  const currentFolderId = ref<string | null>(initialFolderId)
  const isUploading = ref(false)
  const isLoadingDocuments = ref(false)
  const currentFolderName = ref('')
  const uploadError = ref('')
  const uploadSuccess = ref('')
  const highlightedDocumentId = ref<string | null>(null)
  const uploadInput = ref<HTMLInputElement | null>(null)
  const statusPoller = ref<number | null>(null)

  const showNewFolderDialog = ref(false)
  const newFolderName = ref('')
  const newFolderParentId = ref<string | null>(null)

  const showRenameDialog = ref(false)
  const renameFolderName = ref('')
  const renamingFolder = ref<FolderTree | null>(null)

  const showDeleteDialog = ref(false)
  const deletingFolder = ref<FolderTree | null>(null)

  const showDocumentDialog = ref(false)
  const activeDocument = ref<Document | null>(null)
  const activeDocumentContent = ref('')
  const isLoadingDocumentContent = ref(false)
  const activeDocumentChunks = ref<DocumentChunk[]>([])
  const isLoadingDocumentChunks = ref(false)
  const chunkDrafts = ref<Record<string, string>>({})
  const chunkEditing = ref<Record<string, boolean>>({})
  const chunkSaving = ref<Record<string, boolean>>({})
  const chunkEditError = ref<Record<string, string>>({})

  const folderMap = computed(() => {
    const map = new Map<string, FolderTree>()

    function walk(nodes: FolderTree[]) {
      for (const node of nodes) {
        map.set(node.id, node)
        if (node.children?.length) {
          walk(node.children)
        }
      }
    }

    walk(folders.value)
    return map
  })

  const currentFolder = computed(() => {
    if (!currentFolderId.value) return null
    return folderMap.value.get(currentFolderId.value) ?? null
  })

  const isRoot = computed(() => currentFolderId.value === null)

  const visibleFolders = computed(() => {
    if (isRoot.value) return folders.value
    return currentFolder.value?.children ?? []
  })

  const breadcrumbs = computed(() => {
    const items: Array<{ id: string | null; name: string }> = [{ id: null, name: '根目录' }]

    if (!currentFolder.value) {
      return items
    }

    const chain: FolderTree[] = []
    let cursor: FolderTree | null = currentFolder.value

    while (cursor) {
      chain.unshift(cursor)
      cursor = cursor.parent_id ? folderMap.value.get(cursor.parent_id) ?? null : null
    }

    for (const folder of chain) {
      items.push({ id: folder.id, name: folder.name })
    }

    return items
  })

  const explorerTitle = computed(() => {
    if (isRoot.value) {
      return `${visibleFolders.value.length} 个文件夹`
    }

    return `${visibleFolders.value.length} 个文件夹 · ${documents.value.length} 个文件`
  })

  async function loadFolders() {
    try {
      const response = await folderApi.getTree()
      folders.value = response.data
    } catch (error) {
      console.error('加载文件夹失败:', error)
    }
  }

  async function loadDocumentsByFolder(folderId: string | null) {
    isLoadingDocuments.value = true
    try {
      const response = await documentApi.list(folderId)
      documents.value = response.data
    } catch (error) {
      console.error('加载文档失败:', error)
    } finally {
      isLoadingDocuments.value = false
    }
  }

  async function persistCurrentFolder(folderId: string | null) {
    if (folderId) {
      localStorage.setItem(KNOWLEDGE_FOLDER_STORAGE_KEY, folderId)
    } else {
      localStorage.removeItem(KNOWLEDGE_FOLDER_STORAGE_KEY)
    }

    const nextQuery = { ...route.query }
    if (folderId) {
      nextQuery.folder_id = folderId
    } else {
      delete nextQuery.folder_id
    }

    await router.replace({ query: nextQuery })
  }

  async function enterFolder(folder: FolderTree) {
    currentFolderId.value = folder.id
    currentFolderName.value = folder.name
    await persistCurrentFolder(folder.id)
    await loadDocumentsByFolder(folder.id)
  }

  async function goToFolder(folderId: string | null) {
    currentFolderId.value = folderId
    currentFolderName.value = folderId ? (folderMap.value.get(folderId)?.name ?? '') : ''
    await persistCurrentFolder(folderId)
    await loadDocumentsByFolder(folderId)
  }

  async function goBack() {
    if (!currentFolder.value) return
    await goToFolder(currentFolder.value.parent_id)
  }

  function triggerUpload() {
    if (isRoot.value) return
    uploadInput.value?.click()
  }

  function stopStatusPolling() {
    if (statusPoller.value !== null) {
      window.clearInterval(statusPoller.value)
      statusPoller.value = null
    }
  }

  function getStatusLabel(status?: Document['ingestion_status'], isIndexed?: boolean) {
    if (status === 'failed') return 'FAILED'
    if (status === 'warning') return 'WARNING'
    if (status === 'ready' || isIndexed) return 'READY'
    if (status === 'indexing') return 'INDEXING'
    if (status === 'parsing') return 'PARSING'
    return 'UPLOADED'
  }

  async function refreshActiveFolder() {
    await loadDocumentsByFolder(currentFolderId.value)
  }

  function clearUploadFeedbackLater() {
    window.setTimeout(() => {
      uploadSuccess.value = ''
      highlightedDocumentId.value = null
    }, 4000)
  }

  function startStatusPolling() {
    stopStatusPolling()
    statusPoller.value = window.setInterval(async () => {
      await refreshActiveFolder()
      const hasPending = documents.value.some((doc) => !['ready', 'warning', 'failed'].includes(doc.ingestion_status ?? (doc.is_indexed ? 'ready' : 'uploaded')))
      if (!hasPending) {
        stopStatusPolling()
      }
    }, 2500)
  }

  async function handleUpload(event: Event) {
    const target = event.target as HTMLInputElement
    const file = target.files?.[0]
    if (!file) return

    if (!currentFolderId.value) {
      uploadError.value = '请先进入目标文件夹后再上传文件'
      window.setTimeout(() => {
        uploadError.value = ''
      }, 3000)
      target.value = ''
      return
    }

    isUploading.value = true
    uploadError.value = ''
    uploadSuccess.value = ''
    highlightedDocumentId.value = null
    try {
      const response = await documentApi.upload(file, currentFolderId.value)
      const optimisticDocument: Document = {
        id: response.data.id,
        title: response.data.title,
        filename: file.name,
        file_type: file.name.split('.').pop()?.toLowerCase() ?? '',
        file_size: file.size,
        chunk_count: response.data.chunk_count,
        is_indexed: false,
        ingestion_status: response.data.ingestion_status ?? 'uploaded',
        ingestion_error: response.data.ingestion_error ?? null,
        indexed_at: response.data.indexed_at ?? null,
        folder_id: currentFolderId.value,
        created_at: new Date().toISOString(),
      }
      const hasDocument = documents.value.some((doc) => doc.id === optimisticDocument.id)
      if (!hasDocument) {
        documents.value = [optimisticDocument, ...documents.value]
      }
      await refreshActiveFolder()
      if (!documents.value.some((doc) => doc.id === optimisticDocument.id)) {
        documents.value = [optimisticDocument, ...documents.value]
      }
      highlightedDocumentId.value = response.data.id
      const folderName = currentFolderName.value || folderMap.value.get(currentFolderId.value ?? '')?.name || currentFolder.value?.name || '当前文件夹'
      uploadSuccess.value = `已上传到 ${folderName}`
      if (response.data.ingestion_error) {
        uploadError.value = response.data.ingestion_error
      }
      clearUploadFeedbackLater()
      startStatusPolling()
    } catch (error) {
      if (axios.isAxiosError(error)) {
        const detail = error.response?.data?.detail
        const requestId = error.response?.data?.request_id || (error as AxiosError & { requestId?: string }).requestId
        uploadError.value = [detail, requestId ? `请求ID: ${requestId}` : ''].filter(Boolean).join(' · ') || '上传失败，请稍后重试'
      } else {
        uploadError.value = '上传失败，请稍后重试'
      }
      console.error('上传失败:', error)
    } finally {
      isUploading.value = false
      target.value = ''
    }
  }

  async function handleDeleteDocument(id: string) {
    try {
      await documentApi.delete(id)
      documents.value = documents.value.filter((doc) => doc.id !== id)

      if (activeDocument.value?.id === id) {
        closeDocumentDialog()
      }
    } catch (error) {
      console.error('删除失败:', error)
    }
  }

  function openNewFolderDialog(parentId: string | null = null) {
    newFolderParentId.value = parentId
    newFolderName.value = ''
    showNewFolderDialog.value = true
  }

  async function createFolder() {
    if (!newFolderName.value.trim()) return

    try {
      await folderApi.create({
        name: newFolderName.value.trim(),
        parent_id: newFolderParentId.value,
      })
      await loadFolders()
      showNewFolderDialog.value = false
    } catch (error) {
      console.error('创建文件夹失败:', error)
    }
  }

  function openRenameDialog(folder: FolderTree) {
    renamingFolder.value = folder
    renameFolderName.value = folder.name
    showRenameDialog.value = true
  }

  async function renameFolder() {
    if (!renamingFolder.value || !renameFolderName.value.trim()) return

    try {
      await folderApi.rename(renamingFolder.value.id, { name: renameFolderName.value.trim() })
      await loadFolders()
      showRenameDialog.value = false
      renamingFolder.value = null
    } catch (error) {
      console.error('重命名文件夹失败:', error)
    }
  }

  function openDeleteDialog(folder: FolderTree) {
    deletingFolder.value = folder
    showDeleteDialog.value = true
  }

  function isFolderInTree(nodes: FolderTree[], targetId: string) {
    for (const node of nodes) {
      if (node.id === targetId) return true
      if (node.children?.length && isFolderInTree(node.children, targetId)) return true
    }
    return false
  }

  async function deleteFolder() {
    if (!deletingFolder.value) return

    const deletingId = deletingFolder.value.id
    const fallbackParentId = deletingFolder.value.parent_id

    try {
      await folderApi.delete(deletingId)
      await loadFolders()

      if (currentFolderId.value && !isFolderInTree(folders.value, currentFolderId.value)) {
        currentFolderId.value = fallbackParentId
      }

      await loadDocumentsByFolder(currentFolderId.value)
      showDeleteDialog.value = false
      deletingFolder.value = null
    } catch (error) {
      console.error('删除文件夹失败:', error)
    }
  }

  async function loadDocumentChunks(documentId: string) {
    isLoadingDocumentChunks.value = true
    try {
      const response = await documentApi.getChunks(documentId)
      activeDocumentChunks.value = response.data
      chunkDrafts.value = Object.fromEntries(response.data.map((chunk) => [chunk.id, chunk.content]))
      chunkEditing.value = {}
      chunkSaving.value = {}
      chunkEditError.value = Object.fromEntries(response.data.map((chunk) => [chunk.id, '']))
    } catch (error) {
      console.error('加载文档切片失败:', error)
      activeDocumentChunks.value = []
    } finally {
      isLoadingDocumentChunks.value = false
    }
  }

  function startChunkEdit(chunk: DocumentChunk) {
    chunkEditing.value[chunk.id] = true
    chunkDrafts.value[chunk.id] = chunk.content
    chunkEditError.value[chunk.id] = ''
  }

  function cancelChunkEdit(chunkId: string) {
    const chunk = activeDocumentChunks.value.find((item) => item.id === chunkId)
    if (!chunk) return
    chunkEditing.value[chunkId] = false
    chunkDrafts.value[chunkId] = chunk.content
    chunkEditError.value[chunkId] = ''
  }

  async function saveChunkEdit(chunkId: string) {
    if (!activeDocument.value) return
    const nextContent = chunkDrafts.value[chunkId] ?? ''
    chunkSaving.value[chunkId] = true
    chunkEditError.value[chunkId] = ''

    try {
      const response = await documentApi.updateChunk(activeDocument.value.id, chunkId, { content: nextContent })
      activeDocumentChunks.value = activeDocumentChunks.value.map((chunk) => (
        chunk.id === chunkId ? response.data : chunk
      ))
      chunkDrafts.value[chunkId] = response.data.content
      chunkEditing.value[chunkId] = false
    } catch (error) {
      console.error('保存文档切片失败:', error)
      chunkEditError.value[chunkId] = '切片保存失败，请稍后重试'
    } finally {
      chunkSaving.value[chunkId] = false
    }
  }

  async function openDocument(doc: Document) {
    activeDocument.value = doc
    activeDocumentContent.value = ''
    activeDocumentChunks.value = []
    chunkDrafts.value = {}
    chunkEditing.value = {}
    chunkSaving.value = {}
    chunkEditError.value = {}
    showDocumentDialog.value = true
    isLoadingDocumentContent.value = true

    const contentPromise = (async () => {
      try {
        const response = await documentApi.getContent(doc.id)
        const content = response.data as string | { content?: string }
        activeDocumentContent.value = typeof content === 'string' ? content : content.content ?? ''
      } catch (error) {
        console.error('加载文档内容失败:', error)
        activeDocumentContent.value = '暂时无法加载文档内容。'
      } finally {
        isLoadingDocumentContent.value = false
      }
    })()

    const chunkPromise = loadDocumentChunks(doc.id)
    await Promise.all([contentPromise, chunkPromise])
  }

  function closeDocumentDialog() {
    showDocumentDialog.value = false
    activeDocument.value = null
    activeDocumentContent.value = ''
    activeDocumentChunks.value = []
    chunkDrafts.value = {}
    chunkEditing.value = {}
    chunkSaving.value = {}
    chunkEditError.value = {}
  }

  function getFileTypeColor(type: string) {
    const colors: Record<string, string> = {
      pdf: '#f87171',
      md: '#60a5fa',
      txt: '#34d399',
      docx: '#a78bfa',
      doc: '#c084fc',
      csv: '#fbbf24',
      xlsx: '#22c55e',
    }
    return colors[type] || '#9ca3af'
  }

  function formatFileSize(fileSize: number) {
    if (fileSize < 1024) return `${fileSize} B`
    if (fileSize < 1024 * 1024) return `${(fileSize / 1024).toFixed(1)} KB`
    return `${(fileSize / (1024 * 1024)).toFixed(1)} MB`
  }

  function formatDate(date: string) {
    return new Date(date).toLocaleDateString('zh-CN', {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
    })
  }

  onMounted(async () => {
    await loadFolders()
    const persistedFolderId = currentFolderId.value
    if (persistedFolderId && folderMap.value.has(persistedFolderId)) {
      currentFolderName.value = folderMap.value.get(persistedFolderId)?.name ?? ''
      await loadDocumentsByFolder(persistedFolderId)
      return
    }
    currentFolderId.value = null
    currentFolderName.value = ''
    await persistCurrentFolder(null)
    await loadDocumentsByFolder(null)
  })

  onBeforeUnmount(() => {
    stopStatusPolling()
  })

  return {
    folders,
    documents,
    currentFolderId,
    isUploading,
    isLoadingDocuments,
    uploadError,
    uploadSuccess,
    highlightedDocumentId,
    uploadInput,
    showNewFolderDialog,
    newFolderName,
    newFolderParentId,
    showRenameDialog,
    renameFolderName,
    renamingFolder,
    showDeleteDialog,
    deletingFolder,
    showDocumentDialog,
    activeDocument,
    activeDocumentContent,
    isLoadingDocumentContent,
    activeDocumentChunks,
    isLoadingDocumentChunks,
    chunkDrafts,
    chunkEditing,
    chunkSaving,
    chunkEditError,
    currentFolder,
    isRoot,
    visibleFolders,
    breadcrumbs,
    explorerTitle,
    enterFolder,
    goToFolder,
    goBack,
    triggerUpload,
    handleUpload,
    handleDeleteDocument,
    openNewFolderDialog,
    createFolder,
    openRenameDialog,
    renameFolder,
    openDeleteDialog,
    deleteFolder,
    openDocument,
    closeDocumentDialog,
    startChunkEdit,
    cancelChunkEdit,
    saveChunkEdit,
    getFileTypeColor,
    formatFileSize,
    formatDate,
    getStatusLabel,
  }
}
