import { computed, nextTick, ref } from 'vue'

import {
  fetchAgentAssetSpreadsheetBlob,
  fetchAgentAssetSpreadsheetChangeRecords,
  fetchAgentAssetSpreadsheetOnlyOfficeConfig,
  importAgentAssetSpreadsheetContent
} from '../../services/agentAssets.js'
import { loadOnlyOfficeApi } from '../../services/onlyoffice.js'
import { buildOnlyOfficeEditorConfig } from './onlyOfficePreviewConfig.js'
import { buildSpreadsheetChangeRecordKey } from './auditViewRuntimeModel.js'
import {
  formatDateTime,
  formatSpreadsheetChangeSummary,
  normalizeText,
  resolveDiffChangeMeta
} from './auditViewModel.js'

export function useAuditSpreadsheetEditor({
  selectedSkill,
  selectedSkillUsesSpreadsheet,
  canEditSpreadsheetInline,
  canUploadSpreadsheet,
  canDownloadSpreadsheet,
  selectedSpreadsheetFileName,
  actionState,
  refreshCurrentAssets,
  loadSelectedAssetDetail,
  resolveActor,
  toast
}) {
  const spreadsheetUploadInput = ref(null)
  const spreadsheetOnlyOfficeLoading = ref(false)
  const spreadsheetOnlyOfficeError = ref('')
  const spreadsheetOnlyOfficeEditor = ref(null)
  const spreadsheetOnlyOfficeReady = ref(false)
  const spreadsheetOnlyOfficeHostId = ref('audit-rule-onlyoffice')
  const spreadsheetChangeRecordsByAsset = ref({})
  const spreadsheetChangeDetailOpen = ref(false)
  const selectedSpreadsheetChangeRecord = ref(null)

  let spreadsheetOnlyOfficeMountSeq = 0
  let spreadsheetOnlyOfficeLoadTimer = null
  let spreadsheetOnlyOfficeHadLocalEdits = false
  let spreadsheetOnlyOfficeSyncSeq = 0
  let spreadsheetOnlyOfficeChangePollTimer = null

  const selectedSpreadsheetChangeRecords = computed(() => {
    if (!selectedSkillUsesSpreadsheet.value || !selectedSkill.value?.id) {
      return []
    }
    return (spreadsheetChangeRecordsByAsset.value[selectedSkill.value.id] || [])
      .filter((item) => item?.changed_at)
      .map((item) => {
        const sheetNames = [
          ...(Array.isArray(item.sheet_changes)
            ? item.sheet_changes.map((change) => normalizeText(change.sheet_name))
            : []),
          ...(Array.isArray(item.cell_changes)
            ? item.cell_changes.map((change) => normalizeText(change.sheet_name))
            : [])
        ].filter(Boolean)
        const changedSheetNames = [...new Set(sheetNames)]
        const previewChanges = Array.isArray(item.cell_changes) ? item.cell_changes.slice(0, 3) : []
        return {
          ...item,
          time: formatDateTime(item.changed_at),
          summary: formatSpreadsheetChangeSummary(item.summary),
          changeCountLabel: item.changed_cell_count
            ? `${item.changed_cell_count} 处改动`
            : `${item.changed_sheet_count || changedSheetNames.length || 0} 个工作表`,
          changedSheetNames,
          sheetPreview: changedSheetNames.slice(0, 4),
          remainingSheetCount: Math.max(changedSheetNames.length - 4, 0),
          previewChanges,
          remainingChangeCount: Math.max((item.changed_cell_count || 0) - previewChanges.length, 0)
        }
      })
  })

  const selectedSpreadsheetChangeSheetRows = computed(() =>
    Array.isArray(selectedSpreadsheetChangeRecord.value?.sheet_changes)
      ? selectedSpreadsheetChangeRecord.value.sheet_changes.map((item) => ({
          ...item,
          meta: resolveDiffChangeMeta(item.change_type)
        }))
      : []
  )

  const selectedSpreadsheetChangeCellRows = computed(() =>
    Array.isArray(selectedSpreadsheetChangeRecord.value?.cell_changes)
      ? selectedSpreadsheetChangeRecord.value.cell_changes.map((item) => ({
          ...item,
          meta: resolveDiffChangeMeta(item.change_type)
        }))
      : []
  )

  function stopSpreadsheetOnlyOfficeChangeSync() {
    if (spreadsheetOnlyOfficeChangePollTimer) {
      window.clearTimeout(spreadsheetOnlyOfficeChangePollTimer)
      spreadsheetOnlyOfficeChangePollTimer = null
    }
  }

  function destroySpreadsheetOnlyOfficeEditor() {
    if (spreadsheetOnlyOfficeLoadTimer) {
      window.clearTimeout(spreadsheetOnlyOfficeLoadTimer)
      spreadsheetOnlyOfficeLoadTimer = null
    }
    stopSpreadsheetOnlyOfficeChangeSync()
    spreadsheetOnlyOfficeHadLocalEdits = false
    spreadsheetOnlyOfficeSyncSeq += 1
    if (spreadsheetOnlyOfficeEditor.value?.destroyEditor) {
      spreadsheetOnlyOfficeEditor.value.destroyEditor()
    }
    spreadsheetOnlyOfficeEditor.value = null
    spreadsheetOnlyOfficeReady.value = false
  }

  function getLatestSpreadsheetChangeKey(assetId) {
    return buildSpreadsheetChangeRecordKey(spreadsheetChangeRecordsByAsset.value[assetId] || [])
  }

  async function loadSpreadsheetChangeRecords(assetId) {
    if (!assetId) {
      return
    }
    const payload = await fetchAgentAssetSpreadsheetChangeRecords(assetId, 30)
    spreadsheetChangeRecordsByAsset.value = {
      ...spreadsheetChangeRecordsByAsset.value,
      [assetId]: Array.isArray(payload) ? payload : []
    }
  }

  async function refreshSpreadsheetChangeRecordsAfterSave(assetId, previousLatestKey = '', attempt = 0) {
    const normalizedAssetId = normalizeText(assetId)
    if (!normalizedAssetId || selectedSkill.value?.id !== normalizedAssetId) {
      return false
    }

    await loadSpreadsheetChangeRecords(normalizedAssetId)
    const nextLatestKey = getLatestSpreadsheetChangeKey(normalizedAssetId)
    if (nextLatestKey && nextLatestKey !== previousLatestKey) {
      return true
    }
    if (attempt >= 9) {
      return false
    }
    await new Promise((resolve) => window.setTimeout(resolve, 800))
    return refreshSpreadsheetChangeRecordsAfterSave(normalizedAssetId, previousLatestKey, attempt + 1)
  }

  function scheduleSpreadsheetOnlyOfficeChangeSync(assetId, attempt = 0) {
    const normalizedAssetId = normalizeText(assetId)
    if (!normalizedAssetId) {
      return
    }

    const syncSeq = ++spreadsheetOnlyOfficeSyncSeq
    stopSpreadsheetOnlyOfficeChangeSync()
    const previousLatestChangeKey = getLatestSpreadsheetChangeKey(normalizedAssetId)

    const runSync = async () => {
      if (syncSeq !== spreadsheetOnlyOfficeSyncSeq || selectedSkill.value?.id !== normalizedAssetId) {
        return
      }

      try {
        const changeRecordRefreshed = await refreshSpreadsheetChangeRecordsAfterSave(
          normalizedAssetId,
          previousLatestChangeKey
        )
        if (changeRecordRefreshed) {
          await refreshCurrentAssets()
          stopSpreadsheetOnlyOfficeChangeSync()
          return
        }
      } catch {
        // 临时轮询失败不打断编辑器，继续在窗口期内重试。
      }

      if (syncSeq !== spreadsheetOnlyOfficeSyncSeq || selectedSkill.value?.id !== normalizedAssetId) {
        return
      }
      if (attempt >= 29) {
        return
      }
      spreadsheetOnlyOfficeChangePollTimer = window.setTimeout(() => {
        scheduleSpreadsheetOnlyOfficeChangeSync(normalizedAssetId, attempt + 1)
      }, 2000)
    }

    spreadsheetOnlyOfficeChangePollTimer = window.setTimeout(() => {
      runSync().catch(() => {})
    }, attempt === 0 ? 800 : 2000)
  }

  function isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId) {
    return (
      mountSeq !== spreadsheetOnlyOfficeMountSeq ||
      !selectedSkillUsesSpreadsheet.value ||
      selectedSkill.value?.id !== assetId ||
      selectedSkill.value?.loading
    )
  }

  async function mountSpreadsheetOnlyOfficeEditor(retryAttempt = 0) {
    if (!selectedSkillUsesSpreadsheet.value || !selectedSkill.value?.id || selectedSkill.value?.loading) {
      destroySpreadsheetOnlyOfficeEditor()
      return
    }

    const mountSeq = ++spreadsheetOnlyOfficeMountSeq
    const assetId = selectedSkill.value.id
    const editable = canEditSpreadsheetInline.value

    spreadsheetOnlyOfficeLoading.value = true
    spreadsheetOnlyOfficeError.value = ''
    spreadsheetOnlyOfficeReady.value = false
    destroySpreadsheetOnlyOfficeEditor()

    try {
      const payload = await fetchAgentAssetSpreadsheetOnlyOfficeConfig(assetId)
      if (isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)) {
        return
      }

      await loadOnlyOfficeApi(payload.documentServerUrl)
      if (isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)) {
        return
      }
      if (!window.DocsAPI?.DocEditor) {
        throw new Error('表格编辑器未正确加载。')
      }

      // ONLYOFFICE 会改写宿主节点；每次挂载使用新 id 避免复用脏容器。
      spreadsheetOnlyOfficeHostId.value = `audit-rule-onlyoffice-${assetId}-${mountSeq}`
      await nextTick()
      if (isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)) {
        return
      }

      const config = buildOnlyOfficeEditorConfig(payload.config, {
        viewportHeight: window.innerHeight,
        editable,
        fillContainer: true
      })
      const upstreamEvents = config.events || {}
      spreadsheetOnlyOfficeLoadTimer = window.setTimeout(() => {
        if (isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)) {
          return
        }
        if (retryAttempt < 1) {
          destroySpreadsheetOnlyOfficeEditor()
          spreadsheetOnlyOfficeLoading.value = true
          window.setTimeout(() => {
            mountSpreadsheetOnlyOfficeEditor(retryAttempt + 1).catch(() => {})
          }, 600)
          return
        }
        spreadsheetOnlyOfficeError.value = '表格加载超时，请退出详情后重试。'
        spreadsheetOnlyOfficeLoading.value = false
        destroySpreadsheetOnlyOfficeEditor()
      }, 15000)
      config.events = {
        ...upstreamEvents,
        onAppReady(event) {
          if (isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)) {
            return
          }
          if (spreadsheetOnlyOfficeLoadTimer) {
            window.clearTimeout(spreadsheetOnlyOfficeLoadTimer)
            spreadsheetOnlyOfficeLoadTimer = null
          }
          spreadsheetOnlyOfficeReady.value = true
          spreadsheetOnlyOfficeLoading.value = false
          upstreamEvents.onAppReady?.(event)
        },
        onError(event) {
          if (isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)) {
            return
          }
          if (spreadsheetOnlyOfficeLoadTimer) {
            window.clearTimeout(spreadsheetOnlyOfficeLoadTimer)
            spreadsheetOnlyOfficeLoadTimer = null
          }
          const errorCode = event?.data?.errorCode
          const errorDescription = event?.data?.errorDescription
          spreadsheetOnlyOfficeError.value = errorDescription
            ? `表格加载失败：${errorDescription}`
            : `表格加载失败${errorCode ? `（错误码 ${errorCode}）` : '。'}`
          spreadsheetOnlyOfficeLoading.value = false
          upstreamEvents.onError?.(event)
        },
        onDocumentStateChange(event) {
          const hasChanges = Boolean(event?.data)
          if (hasChanges) {
            spreadsheetOnlyOfficeHadLocalEdits = true
            if (!spreadsheetOnlyOfficeChangePollTimer) {
              scheduleSpreadsheetOnlyOfficeChangeSync(assetId)
            }
          } else if (
            spreadsheetOnlyOfficeHadLocalEdits &&
            editable &&
            !isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)
          ) {
            spreadsheetOnlyOfficeHadLocalEdits = false
            scheduleSpreadsheetOnlyOfficeChangeSync(assetId)
          }
          upstreamEvents.onDocumentStateChange?.(event)
        }
      }
      spreadsheetOnlyOfficeEditor.value = new window.DocsAPI.DocEditor(
        spreadsheetOnlyOfficeHostId.value,
        config
      )
      if (isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)) {
        destroySpreadsheetOnlyOfficeEditor()
      }
    } catch (error) {
      if (isSpreadsheetOnlyOfficeMountStale(mountSeq, assetId)) {
        return
      }
      spreadsheetOnlyOfficeError.value = error?.message || '规则表加载失败，请稍后重试。'
      spreadsheetOnlyOfficeLoading.value = false
      toast(spreadsheetOnlyOfficeError.value)
    }
  }

  function triggerSpreadsheetUpload() {
    if (!canUploadSpreadsheet.value) {
      return
    }
    spreadsheetUploadInput.value?.click?.()
  }

  async function downloadSpreadsheetFile() {
    if (!canDownloadSpreadsheet.value || !selectedSkill.value?.id) {
      return
    }

    actionState.value = 'download-spreadsheet'
    try {
      const blob = await fetchAgentAssetSpreadsheetBlob(
        selectedSkill.value.id,
        'attachment'
      )
      const objectUrl = URL.createObjectURL(blob)
      const anchor = document.createElement('a')
      anchor.href = objectUrl
      anchor.download = selectedSpreadsheetFileName.value || '规则表.xlsx'
      document.body.appendChild(anchor)
      anchor.click()
      anchor.remove()
      URL.revokeObjectURL(objectUrl)
    } catch (error) {
      toast(error?.message || '规则表下载失败，请稍后重试。')
    } finally {
      actionState.value = ''
    }
  }

  async function uploadSpreadsheetFile(file) {
    if (!file || !selectedSkill.value?.id || !canUploadSpreadsheet.value) {
      return
    }

    actionState.value = 'upload-spreadsheet'
    try {
      await importAgentAssetSpreadsheetContent(selectedSkill.value.id, file, {
        actor: resolveActor()
      })
      await refreshCurrentAssets()
      await loadSelectedAssetDetail(selectedSkill.value.id)
      await loadSpreadsheetChangeRecords(selectedSkill.value.id)
      toast(`已导入 ${file.name} 的表格内容，右侧会记录本次修改。`)
    } catch (error) {
      toast(error?.message || '规则表内容导入失败，请稍后重试。')
    } finally {
      actionState.value = ''
      spreadsheetUploadInput.value?.reset?.()
    }
  }

  async function handleSpreadsheetFileInput(event) {
    await uploadSpreadsheetFile(event?.target?.files?.[0] || null)
  }

  function openSpreadsheetChangeDetail(item) {
    if (!item?.changed_at) {
      return
    }
    selectedSpreadsheetChangeRecord.value = item
    spreadsheetChangeDetailOpen.value = true
  }

  function closeSpreadsheetChangeDetail() {
    spreadsheetChangeDetailOpen.value = false
  }

  return {
    spreadsheetUploadInput,
    spreadsheetOnlyOfficeLoading,
    spreadsheetOnlyOfficeError,
    spreadsheetOnlyOfficeReady,
    spreadsheetOnlyOfficeHostId,
    spreadsheetChangeDetailOpen,
    selectedSpreadsheetChangeRecord,
    selectedSpreadsheetChangeRecords,
    selectedSpreadsheetChangeSheetRows,
    selectedSpreadsheetChangeCellRows,
    destroySpreadsheetOnlyOfficeEditor,
    mountSpreadsheetOnlyOfficeEditor,
    triggerSpreadsheetUpload,
    downloadSpreadsheetFile,
    uploadSpreadsheetFile,
    handleSpreadsheetFileInput,
    loadSpreadsheetChangeRecords,
    openSpreadsheetChangeDetail,
    closeSpreadsheetChangeDetail
  }
}
