import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'

import ConfirmDialog from '../../components/shared/ConfirmDialog.vue'
import EnterprisePagination from '../../components/shared/EnterprisePagination.vue'
import TableLoadingState from '../../components/shared/TableLoadingState.vue'
import { useSystemState } from '../../composables/useSystemState.js'
import { useToast } from '../../composables/useToast.js'
import {
  deleteKnowledgeDocument,
  fetchKnowledgeDocument,
  fetchKnowledgeDocumentBlob,
  fetchKnowledgeLibrary,
  fetchKnowledgeOnlyOfficeConfig,
  syncKnowledgeLibrary,
  uploadKnowledgeDocument
} from '../../services/knowledge.js'
import { loadOnlyOfficeApi } from '../../services/onlyoffice.js'
import { isManagerUser } from '../../utils/accessControl.js'
import {
  buildExcelPreviewTable,
  buildPreviewMetaLine,
  buildPreviewSecondaryMetaLine
} from './policiesPreviewFormatters.js'
import {
  canUseOnlyOfficePreview,
  resolveKnowledgePreviewMode,
  shouldRenderOnlyOfficeHost,
  shouldRenderOnlyOfficePreview
} from './knowledgePreviewMode.js'
import { resolveKnowledgePreviewLayoutState } from './knowledgePreviewLayout.js'
import {
  resolveInitialKnowledgeFolder,
  resolveKnowledgeFolderIcon
} from './knowledgeFolderSelection.js'
import { buildOnlyOfficePreviewConfig } from './onlyOfficePreviewConfig.js'

const KNOWLEDGE_POLL_INTERVAL_MS = 5000

function triggerFileDownload(blob, filename) {
  const url = URL.createObjectURL(blob)
  const anchor = document.createElement('a')
  anchor.href = url
  anchor.download = filename
  anchor.click()
  URL.revokeObjectURL(url)
}

let bodyOverflowSnapshot = ''
let bodyOverscrollBehaviorSnapshot = ''
let libraryPollTimer = 0

function setBodyScrollLocked(isLocked) {
  if (typeof document === 'undefined') {
    return
  }

  const { body } = document
  if (!body) {
    return
  }

  if (isLocked) {
    if (body.dataset.knowledgePreviewLocked === 'true') {
      return
    }

    bodyOverflowSnapshot = body.style.overflow
    bodyOverscrollBehaviorSnapshot = body.style.overscrollBehavior
    body.style.overflow = 'hidden'
    body.style.overscrollBehavior = 'contain'
    body.dataset.knowledgePreviewLocked = 'true'
    return
  }

  if (body.dataset.knowledgePreviewLocked !== 'true') {
    return
  }

  body.style.overflow = bodyOverflowSnapshot
  body.style.overscrollBehavior = bodyOverscrollBehaviorSnapshot
  delete body.dataset.knowledgePreviewLocked
  bodyOverflowSnapshot = ''
  bodyOverscrollBehaviorSnapshot = ''
}

export default {
  name: 'PoliciesView',
  components: {
    ConfirmDialog,
    EnterprisePagination,
    TableLoadingState
  },
  emits: ['summary-change'],
  setup(_, { emit }) {
    const { currentUser } = useSystemState()
    const { toast } = useToast()

    const documentSearch = ref('')
    const activeFolder = ref('')
    const folders = ref([])
    const documents = ref([])
    const selectedDocument = ref(null)
    const currentPage = ref(1)
    const pageSize = ref(10)
    const pageSizes = [10, 20, 50]
    const pageSizeOptions = pageSizes.map((size) => ({ label: `${size} 条/页`, value: size }))
    const loading = ref(false)
    const uploadInput = ref(null)
    const uploading = ref(false)
    const deletingId = ref('')
    const syncingFolder = ref(false)
    const deleteDialogOpen = ref(false)
    const deleteTargetDocument = ref(null)
    const previewLoading = ref(false)
    const previewBlobUrl = ref('')
    const previewError = ref('')
    const onlyOfficeLoading = ref(false)
    const onlyOfficeError = ref('')
    const onlyOfficeAvailable = ref(false)
    const onlyOfficeEditor = ref(null)
    const onlyOfficeHostId = ref('knowledge-onlyoffice-preview')
    const onlyOfficeReadyTimeoutId = ref(0)
    const currentPreviewPageIndex = ref(0)
    const previewDialogPanel = ref(null)

    const isAdmin = computed(() => isManagerUser(currentUser.value))
    const uploadHint = computed(() =>
      isAdmin.value
        ? '支持 PDF / Word / Excel / PPT / 图片 / 文本文件，重复同名文件将自动覆盖并升级版本'
        : '当前账号只有查阅权限，上传、删除和修改仅管理员可用'
    )

    const filteredFolders = computed(() => folders.value)

    const filteredDocuments = computed(() => {
      const key = documentSearch.value.trim()

      return documents.value.filter((doc) => {
        const inFolder = activeFolder.value ? doc.folder === activeFolder.value : true
        const matchesSearch = key ? doc.name.includes(key) : true
        return inFolder && matchesSearch
      })
    })
    const activeFolderDocuments = computed(() =>
      documents.value.filter((doc) => (activeFolder.value ? doc.folder === activeFolder.value : true))
    )
    const activeFolderIngestStats = computed(() => {
      const stats = {
        total: activeFolderDocuments.value.length,
        pending: 0,
        syncing: 0,
        ingested: 0,
        failed: 0
      }

      for (const doc of activeFolderDocuments.value) {
        const stateCode = Number(doc?.stateCode || 0)
        if (stateCode === 2) {
          stats.syncing += 1
        } else if (stateCode === 3) {
          stats.ingested += 1
        } else if (stateCode === 4) {
          stats.failed += 1
        } else {
          stats.pending += 1
        }
      }
      return stats
    })
    const knowledgeSyncButtonLabel = computed(() => {
      if (syncingFolder.value || activeFolderIngestStats.value.syncing > 0) {
        return '归纳中...'
      }
      return '知识归纳'
    })
    const canTriggerKnowledgeSync = computed(
      () =>
        isAdmin.value
        && Boolean(activeFolder.value)
        && activeFolderIngestStats.value.total > 0
        && !syncingFolder.value
        && activeFolderIngestStats.value.syncing === 0
    )

    const totalCount = computed(() => filteredDocuments.value.length)
    const totalPages = computed(() => Math.max(1, Math.ceil(totalCount.value / pageSize.value)))
    const pageSummary = computed(() => `共 ${totalCount.value} 条，目前第 ${currentPage.value} / ${totalPages.value} 页`)
    const visibleDocuments = computed(() => {
      const start = (currentPage.value - 1) * pageSize.value
      return filteredDocuments.value.slice(start, start + pageSize.value)
    })
    const activePreviewPage = computed(() => {
      const pages = selectedDocument.value?.previewPages || []
      return pages[currentPreviewPageIndex.value] || pages[0] || null
    })
    const previewMetaLine = computed(() => buildPreviewMetaLine(selectedDocument.value))
    const previewSecondaryMetaLine = computed(() =>
      buildPreviewSecondaryMetaLine(selectedDocument.value, activePreviewPage.value)
    )
    const previewLayoutState = computed(() =>
      resolveKnowledgePreviewLayoutState(selectedDocument.value)
    )
    const previewMode = computed(() =>
      resolveKnowledgePreviewMode(selectedDocument.value, {
        onlyOfficeAvailable: onlyOfficeAvailable.value
      })
    )
    const shouldRenderOnlyOffice = computed(() =>
      shouldRenderOnlyOfficePreview(selectedDocument.value, {
        onlyOfficeLoading: onlyOfficeLoading.value,
        onlyOfficeAvailable: onlyOfficeAvailable.value,
        onlyOfficeError: onlyOfficeError.value
      })
    )
    const shouldRenderOnlyOfficeHostNode = computed(() =>
      shouldRenderOnlyOfficeHost(selectedDocument.value, {
        onlyOfficeLoading: onlyOfficeLoading.value,
        onlyOfficeAvailable: onlyOfficeAvailable.value,
        onlyOfficeError: onlyOfficeError.value
      })
    )
    const excelPreviewTable = computed(() =>
      selectedDocument.value?.previewKind === 'table'
        ? buildExcelPreviewTable(activePreviewPage.value)
        : { headers: [], rows: [] }
    )

    function revokePreviewBlob() {
      if (previewBlobUrl.value) {
        URL.revokeObjectURL(previewBlobUrl.value)
        previewBlobUrl.value = ''
      }
    }

    function destroyOnlyOfficeEditor() {
      if (onlyOfficeReadyTimeoutId.value) {
        window.clearTimeout(onlyOfficeReadyTimeoutId.value)
        onlyOfficeReadyTimeoutId.value = 0
      }
      if (onlyOfficeEditor.value?.destroyEditor) {
        onlyOfficeEditor.value.destroyEditor()
      }
      onlyOfficeEditor.value = null
    }

    async function mountOnlyOfficeEditor(documentId) {
      onlyOfficeLoading.value = true
      onlyOfficeError.value = ''
      onlyOfficeAvailable.value = false
      destroyOnlyOfficeEditor()

      try {
        const payload = await fetchKnowledgeOnlyOfficeConfig(documentId)
        await loadOnlyOfficeApi(payload.documentServerUrl)
        await nextTick()

        if (!window.DocsAPI?.DocEditor) {
          throw new Error('ONLYOFFICE 编辑器未正确加载。')
        }

        onlyOfficeHostId.value = `knowledge-onlyoffice-preview-${documentId}`
        await nextTick()
        const config = buildOnlyOfficePreviewConfig(payload.config, {
          viewportHeight: window.innerHeight
        })
        const upstreamEvents = config.events || {}
        config.events = {
          ...upstreamEvents,
          onAppReady(event) {
            if (onlyOfficeReadyTimeoutId.value) {
              window.clearTimeout(onlyOfficeReadyTimeoutId.value)
              onlyOfficeReadyTimeoutId.value = 0
            }
            onlyOfficeAvailable.value = true
            onlyOfficeLoading.value = false
            upstreamEvents.onAppReady?.(event)
          },
          onError(event) {
            if (onlyOfficeReadyTimeoutId.value) {
              window.clearTimeout(onlyOfficeReadyTimeoutId.value)
              onlyOfficeReadyTimeoutId.value = 0
            }
            const errorCode = event?.data?.errorCode
            const errorDescription = event?.data?.errorDescription
            const message = errorDescription
              ? `ONLYOFFICE 预览失败：${errorDescription}`
              : `ONLYOFFICE 预览失败${errorCode ? `（错误码 ${errorCode}）` : '。'}`
            onlyOfficeError.value = message
            onlyOfficeLoading.value = false
            console.error('ONLYOFFICE onError', event)
            toast(message)
            upstreamEvents.onError?.(event)
          }
        }
        onlyOfficeEditor.value = new window.DocsAPI.DocEditor(onlyOfficeHostId.value, config)
        onlyOfficeReadyTimeoutId.value = window.setTimeout(() => {
          if (!onlyOfficeAvailable.value && !onlyOfficeError.value) {
            onlyOfficeError.value = 'ONLYOFFICE 预览初始化超时。请检查浏览器是否拦截了 iframe 或混合内容。'
            onlyOfficeLoading.value = false
            toast(onlyOfficeError.value)
          }
        }, 10000)
        return true
      } catch (error) {
        onlyOfficeError.value = error.message || 'ONLYOFFICE 预览加载失败。'
        toast(onlyOfficeError.value)
        return false
      } finally {
        if (onlyOfficeError.value) {
          onlyOfficeLoading.value = false
        }
      }
    }

    async function loadLibrary(options = {}) {
      loading.value = true
      try {
        const payload = await fetchKnowledgeLibrary()
        folders.value = payload.folders || []
        documents.value = payload.documents || []
        emit('summary-change', { totalDocuments: documents.value.length })

        activeFolder.value = resolveInitialKnowledgeFolder(folders.value, activeFolder.value)

        if (options.preserveSelection && selectedDocument.value?.id) {
          const nextDocument = documents.value.find((doc) => doc.id === selectedDocument.value.id)
          const exists = Boolean(nextDocument)
          if (!exists) {
            closePreview()
          } else {
            selectedDocument.value = {
              ...selectedDocument.value,
              ...nextDocument
            }
          }
        }
        syncLibraryPolling()
      } catch (error) {
        emit('summary-change', { totalDocuments: 0 })
        toast(error.message || '知识库加载失败。')
      } finally {
        loading.value = false
      }
    }

    async function selectDocument(documentId) {
      previewLoading.value = true
      previewError.value = ''
      onlyOfficeError.value = ''
      onlyOfficeAvailable.value = false
      revokePreviewBlob()
      destroyOnlyOfficeEditor()

      try {
        const payload = await fetchKnowledgeDocument(documentId)
        selectedDocument.value = payload
        currentPreviewPageIndex.value = 0

        if (canUseOnlyOfficePreview(payload)) {
          previewLoading.value = false
          await mountOnlyOfficeEditor(documentId)
          return
        }

        if (payload.previewKind === 'pdf' || payload.previewKind === 'image') {
          const blob = await fetchKnowledgeDocumentBlob(documentId, 'inline')
          previewBlobUrl.value = URL.createObjectURL(blob)
        }
      } catch (error) {
        previewError.value = error.message || '预览加载失败。'
        toast(previewError.value)
      } finally {
        previewLoading.value = false
      }
    }

    async function handleDownload(document) {
      try {
        const blob = await fetchKnowledgeDocumentBlob(document.id, 'attachment')
        triggerFileDownload(blob, document.name)
      } catch (error) {
        toast(error.message || '下载失败。')
      }
    }

    function patchDocumentState(documentId, patch) {
      documents.value = documents.value.map((doc) =>
        doc.id === documentId ? { ...doc, ...patch } : doc
      )

      if (selectedDocument.value?.id === documentId) {
        selectedDocument.value = {
          ...selectedDocument.value,
          ...patch
        }
      }
      syncLibraryPolling()
    }

    function hasSyncingDocuments() {
      return documents.value.some((doc) => Number(doc?.stateCode || 0) === 2)
    }

    function stopLibraryPolling() {
      if (libraryPollTimer) {
        window.clearInterval(libraryPollTimer)
        libraryPollTimer = 0
      }
    }

    function startLibraryPolling() {
      stopLibraryPolling()
      libraryPollTimer = window.setInterval(() => {
        loadLibrary({ preserveSelection: true })
      }, KNOWLEDGE_POLL_INTERVAL_MS)
    }

    function syncLibraryPolling() {
      if (hasSyncingDocuments()) {
        startLibraryPolling()
        return
      }
      stopLibraryPolling()
    }

    async function handleKnowledgeSync() {
      if (!canTriggerKnowledgeSync.value) {
        return
      }

      syncingFolder.value = true
      try {
        const payload = await syncKnowledgeLibrary({
          folder: activeFolder.value,
          documentIds: [],
          force: true
        })

        const queuedIds = Array.isArray(payload?.document_ids) ? payload.document_ids : []
        for (const documentId of queuedIds) {
          patchDocumentState(documentId, {
            stateCode: 2,
            state: '\u5f52\u7eb3\u4e2d',
            stateTone: 'warning',
            ingestTime: ''
          })
        }

        await loadLibrary({ preserveSelection: true })
        const runHint = payload?.agent_run_id ? `日志编号：${payload.agent_run_id}` : ''
        toast([payload?.summary || '知识归纳任务已提交。', runHint].filter(Boolean).join(' '))
      } catch (error) {
        await loadLibrary({ preserveSelection: true })
        toast(error.message || '\u77e5\u8bc6\u5f52\u7eb3\u89e6\u53d1\u5931\u8d25\u3002')
      } finally {
        syncingFolder.value = false
      }
    }

    function triggerUpload() {
      if (!isAdmin.value || uploading.value) {
        return
      }
      uploadInput.value?.click()
    }

    async function uploadFiles(fileList) {
      const files = Array.from(fileList || []).filter(Boolean)
      if (!files.length || !activeFolder.value || !isAdmin.value) {
        return
      }

      uploading.value = true
      try {
        let latestDocumentId = ''
        for (const file of files) {
          const payload = await uploadKnowledgeDocument({ folder: activeFolder.value, file })
          latestDocumentId = payload.id
        }

        await loadLibrary({ preserveSelection: true })
        toast(files.length > 1 ? `已上传 ${files.length} 个知识库文件。` : '知识库文件已上传。')

        if (latestDocumentId) {
          await selectDocument(latestDocumentId)
        }
      } catch (error) {
        toast(error.message || '上传失败。')
      } finally {
        uploading.value = false
        if (uploadInput.value) {
          uploadInput.value.value = ''
        }
      }
    }

    async function handleFileInput(event) {
      await uploadFiles(event.target.files)
    }

    async function handleDrop(event) {
      if (!isAdmin.value) {
        return
      }
      await uploadFiles(event.dataTransfer?.files)
    }

    async function handleDelete(document) {
      if (!isAdmin.value || deletingId.value) {
        return
      }

      deleteTargetDocument.value = document
      deleteDialogOpen.value = true
    }

    function closeDeleteDialog() {
      if (deletingId.value) {
        return
      }

      deleteDialogOpen.value = false
      deleteTargetDocument.value = null
    }

    async function confirmDeleteDocument() {
      const document = deleteTargetDocument.value
      if (!document || !isAdmin.value || deletingId.value) {
        return
      }

      deletingId.value = document.id
      try {
        await deleteKnowledgeDocument(document.id)
        deleteDialogOpen.value = false
        deleteTargetDocument.value = null
        if (selectedDocument.value?.id === document.id) {
          closePreview()
        }
        await loadLibrary()
        toast('知识库文件已删除。')
      } catch (error) {
        toast(error.message || '删除失败。')
      } finally {
        deletingId.value = ''
      }
    }

    function changePageSize(size) {
      pageSize.value = size
      currentPage.value = 1
    }

    function closePreview() {
      selectedDocument.value = null
      previewLoading.value = false
      previewError.value = ''
      currentPreviewPageIndex.value = 0
      revokePreviewBlob()
      destroyOnlyOfficeEditor()
      onlyOfficeLoading.value = false
      onlyOfficeError.value = ''
      onlyOfficeAvailable.value = false
    }

    function handleWindowKeydown(event) {
      if (event.key === 'Escape' && selectedDocument.value) {
        closePreview()
      }
    }

    function selectPreviewPage(index) {
      currentPreviewPageIndex.value = index
    }

    watch(filteredDocuments, () => {
      currentPage.value = 1

      if (selectedDocument.value && !filteredDocuments.value.some((doc) => doc.id === selectedDocument.value.id)) {
        closePreview()
      }
    })

    watch(activeFolder, () => {
      closePreview()
    })

    watch(
      () => previewLayoutState.value.isPreviewModalOpen,
      async (isAnyOverlayOpen) => {
        setBodyScrollLocked(isAnyOverlayOpen)

        if (previewLayoutState.value.isPreviewModalOpen) {
          await nextTick()
          previewDialogPanel.value?.focus?.()
        }
      }
    )

    onMounted(() => {
      loadLibrary()
      window.addEventListener('keydown', handleWindowKeydown)
    })

    onBeforeUnmount(() => {
      revokePreviewBlob()
      destroyOnlyOfficeEditor()
      setBodyScrollLocked(false)
      stopLibraryPolling()
      window.removeEventListener('keydown', handleWindowKeydown)
    })

    return {
      activeFolder,
      activeFolderIngestStats,
      activePreviewPage,
      canTriggerKnowledgeSync,
      changePageSize,
      closePreview,
      closeDeleteDialog,
      confirmDeleteDocument,
      excelPreviewTable,
      currentPage,
      currentPreviewPageIndex,
      deleteDialogOpen,
      deleteTargetDocument,
      deletingId,
      documentSearch,
      filteredFolders,
      handleDelete,
      handleDownload,
      handleDrop,
      handleFileInput,
      handleKnowledgeSync,
      isAdmin,
      knowledgeSyncButtonLabel,
      loading,
      pageSize,
      pageSizeOptions,
      pageSummary,
      pageSizes,
      onlyOfficeError,
      onlyOfficeHostId,
      onlyOfficeLoading,
      previewDialogPanel,
      previewLayoutState,
      previewMode,
      previewMetaLine,
      previewSecondaryMetaLine,
      previewBlobUrl,
      previewError,
      previewLoading,
      shouldRenderOnlyOffice,
      shouldRenderOnlyOfficeHostNode,
      selectDocument,
      selectPreviewPage,
      selectedDocument,
      resolveKnowledgeFolderIcon,
      syncingFolder,
      totalCount,
      totalPages,
      triggerUpload,
      uploadHint,
      uploadInput,
      uploading,
      visibleDocuments
    }
  }
}
