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

import {
  deleteExpenseClaimItem,
  deleteExpenseClaimItemAttachment,
  uploadExpenseClaimItemAttachment,
  updateExpenseClaimItem
} from '../../services/reimbursements.js'
import {
  buildExpenseDraftIssues,
  buildExpenseItemViewModel,
  isPlaceholderValue,
  isRouteDescriptionExpenseType,
  isSyntheticLocationDisplay,
  isValidIsoDate,
  isValidRouteDescription,
  rebuildExpenseItems,
  resolveExpenseUploadHint
} from './travelRequestDetailExpenseModel.js'
import {
  buildItemClaimRiskState,
  normalizeRiskTone
} from './travelRequestDetailInsights.js'
import {
  buildRecognizedExpenseItemPatch,
  startSmartEntryRecognitionTask,
  subscribeSmartEntryRecognitionTask
} from './travelRequestDetailSmartEntryRecognition.js'

export function useTravelRequestDetailExpenseEditor({
  request,
  expenseItems,
  expenseAttachmentMeta,
  isEditableRequest,
  getActionBusy,
  toast,
  emit,
  attachmentPreviewOpen,
  buildAttachmentRiskNotice,
  closeAttachmentPreview,
  refreshExpenseAttachmentMeta,
  resolveAttachmentMeta,
  resolveClaimRiskFlags,
  applyClaimRiskFlagsPayload
}) {
  const editingExpenseId = ref('')
  const savingExpenseId = ref('')
  const uploadingExpenseId = ref('')
  const deletingAttachmentId = ref('')
  const deletingExpenseId = ref('')
  const pendingUploadExpenseId = ref('')
  const expenseUploadInput = ref(null)
  const smartEntryUploadInput = ref(null)
  const smartEntryUploadDialogOpen = ref(false)
  const smartEntrySelectedFiles = ref([])
  const smartEntryRecognitionBusy = ref(false)
  const smartEntryRecognitionTotal = ref(0)
  const smartEntryRecognitionCompleted = ref(0)
  const smartEntryRecognitionCurrent = ref(0)
  const appliedSmartEntryRecognitionPayloadIds = new Set()
  const notifiedSmartEntryRecognitionTaskIds = new Set()
  let stopSmartEntryRecognitionTask = null

  const expenseEditor = reactive({
    itemDate: '',
    itemType: 'other',
    itemReason: '',
    itemLocation: '',
    itemAmount: '',
    itemNote: '',
    invoiceId: ''
  })

  const expenseTotal = computed(() => {
    const total = expenseItems.value.reduce((sum, item) => {
      const adjustedAmount = Number(item.reimbursableAmount)
      const originalAmount = Number(item.itemAmount || 0)
      return sum + (Number.isFinite(adjustedAmount) ? adjustedAmount : originalAmount)
    }, 0)
    return new Intl.NumberFormat('zh-CN', {
      style: 'currency',
      currency: 'CNY',
      minimumFractionDigits: 0,
      maximumFractionDigits: Number.isInteger(total) ? 0 : 2
    }).format(total)
  })
  const uploadedExpenseCount = computed(() => expenseItems.value.filter((item) => item.attachments.length).length)
  const expenseTableColumnCount = computed(
    () => 7 + (isEditableRequest.value ? 1 : 0)
  )
  const smartEntryRecognitionText = computed(() => {
    const total = smartEntryRecognitionTotal.value
    if (!total) {
      return '附件识别准备中，请稍候。识别完成前暂不可编辑费用明细。'
    }
    const current = Math.min(Math.max(smartEntryRecognitionCurrent.value || 1, 1), total)
    return `附件识别中（${current}/${total}），请稍候。识别完成前暂不可编辑费用明细。`
  })
  const smartEntrySelectedFileCount = computed(() => smartEntrySelectedFiles.value.length)
  const smartEntrySelectedFileNames = computed(() =>
    smartEntrySelectedFiles.value
      .map((file) => String(file?.name || '').trim())
      .filter(Boolean)
  )
  const smartEntrySelectedFileSummary = computed(() => {
    const names = smartEntrySelectedFileNames.value
    if (!names.length) {
      return ''
    }
    if (names.length === 1) {
      return names[0]
    }
    return `已选择 ${names.length} 张附件`
  })
  const smartEntryUploadBusy = computed(() =>
    smartEntryUploadDialogOpen.value && Boolean(uploadingExpenseId.value)
  )

  function resetExpenseEditorFields() {
    editingExpenseId.value = ''
    expenseEditor.itemDate = ''
    expenseEditor.itemType = 'other'
    expenseEditor.itemReason = ''
    expenseEditor.itemLocation = ''
    expenseEditor.itemAmount = ''
    expenseEditor.itemNote = ''
    expenseEditor.invoiceId = ''
  }

  function applyLocalExpenseItemPatch(itemId, patch) {
    expenseItems.value = rebuildExpenseItems(
      expenseItems.value.map((item) => (item.id === itemId ? { ...item, ...patch } : item)),
      request.value
    )
  }

  function resetSmartEntryRecognitionState() {
    smartEntryRecognitionBusy.value = false
    smartEntryRecognitionTotal.value = 0
    smartEntryRecognitionCompleted.value = 0
    smartEntryRecognitionCurrent.value = 0
    if (!pendingUploadExpenseId.value) {
      uploadingExpenseId.value = ''
    }
  }

  function ensureSmartEntryRecognitionItem(entry, patch) {
    const itemId = String(entry?.itemId || '').trim()
    if (!itemId) {
      return null
    }

    const existingItem = expenseItems.value.find((item) => item.id === itemId)
    if (existingItem) {
      return existingItem
    }

    const rawItem = entry?.createdItem || {
      id: itemId,
      invoice_id: patch.invoiceId,
      item_date: patch.itemDate,
      item_type: patch.itemType,
      item_reason: patch.itemReason,
      item_location: patch.itemLocation,
      item_amount: patch.itemAmount,
      attachment_hint: patch.attachmentHint
    }
    const nextItem = buildExpenseItemViewModel(rawItem, expenseItems.value.length, request.value)
    expenseItems.value = rebuildExpenseItems([...expenseItems.value, nextItem], request.value)
    return nextItem
  }

  function applySmartEntryRecognitionPayload(entry) {
    const payloadId = String(entry?.id || '').trim()
    const itemId = String(entry?.itemId || '').trim()
    if (!payloadId || !itemId || appliedSmartEntryRecognitionPayloadIds.has(payloadId)) {
      return
    }

    const itemPatch = buildRecognizedExpenseItemPatch(entry.payload, entry.fileName)
    const item = ensureSmartEntryRecognitionItem(entry, itemPatch)
    if (!item) {
      return
    }

    applyClaimRiskFlagsPayload(entry.payload)
    if (entry.payload?.attachment) {
      expenseAttachmentMeta[itemId] = entry.payload.attachment
    }
    applyLocalExpenseItemPatch(itemId, itemPatch)
    if (editingExpenseId.value === itemId) {
      populateExpenseEditor({ ...item, ...itemPatch })
    }
    appliedSmartEntryRecognitionPayloadIds.add(payloadId)
    emit('request-updated', { claimId: request.value.claimId })
  }

  function syncSmartEntryRecognitionSnapshot(snapshot) {
    if (!snapshot) {
      resetSmartEntryRecognitionState()
      return
    }

    smartEntryRecognitionBusy.value = Boolean(snapshot.busy)
    smartEntryRecognitionTotal.value = snapshot.total || 0
    smartEntryRecognitionCompleted.value = snapshot.completed || 0
    smartEntryRecognitionCurrent.value = snapshot.current || 0
    uploadingExpenseId.value = snapshot.uploadingItemId || ''

    snapshot.payloads.forEach((entry) => applySmartEntryRecognitionPayload(entry))

    if (!snapshot.busy && snapshot.status && !notifiedSmartEntryRecognitionTaskIds.has(snapshot.id)) {
      notifiedSmartEntryRecognitionTaskIds.add(snapshot.id)
      if (snapshot.failedCount && snapshot.successCount) {
        toast(`已完成 ${snapshot.successCount} 张附件识别，${snapshot.failedCount} 张识别失败。`)
      } else if (snapshot.failedCount) {
        toast('附件识别失败，请稍后重试。')
      } else if (snapshot.total > 1) {
        toast(`已完成 ${snapshot.successCount} 张附件的智能录入。`)
      }
    }
  }

  function bindSmartEntryRecognitionTask(claimId = request.value.claimId) {
    if (stopSmartEntryRecognitionTask) {
      stopSmartEntryRecognitionTask()
      stopSmartEntryRecognitionTask = null
    }

    stopSmartEntryRecognitionTask = subscribeSmartEntryRecognitionTask(claimId, syncSmartEntryRecognitionSnapshot)
  }

  function resetSmartEntryRecognitionApplications() {
    appliedSmartEntryRecognitionPayloadIds.clear()
  }

  function resolveExpenseIssues(item) {
    return buildExpenseDraftIssues(item)
  }

  function resolveExpenseRiskState(item) {
    if (uploadingExpenseId.value === item.id) {
      return {
        label: 'AI识别中',
        tone: 'medium',
        headline: 'AI提示：正在分析附件内容',
        summary: '附件已上传，系统正在识别票据内容与风险点，请稍候。',
        points: [],
        suggestion: ''
      }
    }

    const metadata = resolveAttachmentMeta(item)
    const analysis = metadata?.analysis
    if (analysis) {
      return {
        label: analysis.label || '已上传',
        tone: normalizeRiskTone(analysis.severity || 'low'),
        headline: analysis.headline || 'AI提示',
        summary: analysis.summary || '',
        points: Array.isArray(analysis.points) ? analysis.points : [],
        suggestion: analysis.suggestion || ''
      }
    }

    const claimRiskState = buildItemClaimRiskState(item, resolveClaimRiskFlags())
    if (claimRiskState) {
      return claimRiskState
    }

    if (!item.invoiceId) {
      return null
    }

    return {
      label: '已上传',
      tone: 'low',
      headline: 'AI提示：附件已上传',
      summary: '附件已成功保存，当前可继续查看原图并人工核对票据内容。',
      points: [],
      suggestion: ''
    }
  }

  function showExpenseRisk(item) {
    return Boolean(resolveExpenseRiskState(item))
  }

  function isMajorExpenseRisk(item) {
    return normalizeRiskTone(resolveExpenseRiskState(item)?.tone) === 'high'
  }

  function hasExpenseRiskOrAbnormal(item) {
    const state = resolveExpenseRiskState(item)
    return Boolean(
      String(item?.itemNote || '').trim()
      || normalizeRiskTone(state?.tone) !== 'low'
      || item?.tone === 'bad'
    )
  }

  function resolveExpenseRiskIndicatorTitle(item) {
    const state = resolveExpenseRiskState(item)
    const summary = String(state?.summary || state?.headline || '').trim()
    return summary ? `查看风险提示：${summary}` : '查看风险提示'
  }

  function populateExpenseEditor(item) {
    editingExpenseId.value = item.id
    expenseEditor.itemDate = item.itemDate || ''
    expenseEditor.itemType = item.itemType || 'other'
    expenseEditor.itemReason = item.itemReason || (item.desc === '待补充' ? '' : item.desc)
    expenseEditor.itemLocation =
      item.itemLocation || (isSyntheticLocationDisplay(item.detail, item.itemType) ? '' : item.detail)
    expenseEditor.itemAmount = item.itemAmount ? String(item.itemAmount) : ''
    expenseEditor.itemNote = item.itemNote || ''
    expenseEditor.invoiceId = item.invoiceId || ''
  }

  function startExpenseEdit(item) {
    if (!isEditableRequest.value || getActionBusy()) {
      return
    }
    if (item?.isSystemGenerated) {
      toast('系统自动计算的补贴行不能手动编辑。')
      return
    }

    populateExpenseEditor(item)
  }

  function validateExpenseEditor() {
    if (expenseEditor.itemDate && !isValidIsoDate(expenseEditor.itemDate)) {
      return '请输入正确的费用日期，格式为 YYYY-MM-DD。'
    }
    if (isPlaceholderValue(expenseEditor.itemType)) {
      return '请选择费用项目。'
    }
    if (
      !isPlaceholderValue(expenseEditor.itemReason)
      && isRouteDescriptionExpenseType(expenseEditor.itemType)
      && !isValidRouteDescription(expenseEditor.itemReason)
    ) {
      return '行程说明格式应为“起始地-目的地”，例如：广州南-北京南。'
    }

    const amountText = String(expenseEditor.itemAmount || '').trim()
    if (amountText) {
      const amount = Number(amountText)
      if (!Number.isFinite(amount) || amount < 0) {
        return '请输入不小于 0 的费用金额。'
      }
    }
    return ''
  }

  function triggerSmartEntryUpload() {
    if (!isEditableRequest.value || getActionBusy()) {
      return
    }

    if (!request.value.claimId) {
      toast('当前草稿缺少 claimId，暂时无法上传单据。')
      return
    }

    smartEntrySelectedFiles.value = []
    smartEntryUploadDialogOpen.value = true
  }

  function closeSmartEntryUploadDialog() {
    if (smartEntryUploadBusy.value) {
      return
    }
    smartEntryUploadDialogOpen.value = false
    clearSmartEntryFile()
  }

  function chooseSmartEntryFile() {
    if (smartEntryUploadBusy.value) {
      return
    }
    if (smartEntryUploadInput.value) {
      smartEntryUploadInput.value.value = ''
      smartEntryUploadInput.value.click()
    }
  }

  function clearSmartEntryFile() {
    smartEntrySelectedFiles.value = []
    if (smartEntryUploadInput.value) {
      smartEntryUploadInput.value.value = ''
    }
  }

  function handleSmartEntryFileChange(event) {
    const target = event?.target
    const fileList = target?.files
    const files = Array.from(fileList || [])

    if (target) {
      target.value = ''
    }

    if (!files.length) {
      return
    }

    smartEntrySelectedFiles.value = files
  }

  async function confirmSmartEntryUpload() {
    if (smartEntryUploadBusy.value) {
      return
    }
    const files = [...smartEntrySelectedFiles.value]
    if (!files.length) {
      toast('请先选择需要智能录入的附件。')
      return
    }

    smartEntryUploadDialogOpen.value = false
    clearSmartEntryFile()
    const { task, reused } = startSmartEntryRecognitionTask({
      claimId: request.value.claimId,
      files,
      itemSnapshots: expenseItems.value
    })
    if (!task) {
      toast('当前草稿缺少 claimId，暂时无法识别附件。')
      return
    }

    bindSmartEntryRecognitionTask(request.value.claimId)
    toast(reused ? '当前单据已有附件识别任务，请等待识别完成。' : '附件已转入后台识别，费用明细将在识别完成后自动更新。')
  }

  function triggerExpenseUpload(item) {
    if (!isEditableRequest.value || getActionBusy()) {
      return
    }

    if (!request.value.claimId) {
      toast('当前草稿缺少 claimId，暂时无法上传单据。')
      return
    }

    if (item?.isSystemGenerated) {
      toast('系统自动计算的补贴行无需上传附件。')
      return
    }

    if (item?.invoiceId) {
      toast('每条费用明细只能关联一张单据，如需更换请先删除当前单据。')
      return
    }

    pendingUploadExpenseId.value = item.id
    if (expenseUploadInput.value) {
      expenseUploadInput.value.value = ''
      expenseUploadInput.value.click()
    }
  }

  async function uploadExpenseFile(item, file) {
    if (!item || !file) {
      return false
    }
    if (item?.isSystemGenerated) {
      toast('系统自动计算的补贴行无需上传附件。')
      return false
    }

    if (item?.invoiceId) {
      toast('每条费用明细只能关联一张单据，如需更换请先删除当前单据。')
      return false
    }

    uploadingExpenseId.value = item.id

    try {
      const payload = await uploadExpenseClaimItemAttachment(request.value.claimId, item.id, file)
      applyClaimRiskFlagsPayload(payload)
      expenseAttachmentMeta[item.id] = payload?.attachment || null
      const itemPatch = buildRecognizedExpenseItemPatch(payload, file.name)
      applyLocalExpenseItemPatch(item.id, {
        ...itemPatch
      })
      populateExpenseEditor({ ...item, ...itemPatch })

      emit('request-updated', { claimId: request.value.claimId })
      const riskNotice = buildAttachmentRiskNotice(payload?.attachment)
      toast(riskNotice || payload?.message || `${file.name} 已关联到当前费用明细。`)
      return true
    } catch (error) {
      toast(error?.message || '附件上传失败，请稍后重试。')
      return false
    } finally {
      uploadingExpenseId.value = ''
    }
  }

  async function removeExpenseAttachment(item) {
    if (!request.value.claimId || !item?.invoiceId || getActionBusy()) {
      return
    }

    deletingAttachmentId.value = item.id
    try {
      const payload = await deleteExpenseClaimItemAttachment(request.value.claimId, item.id)
      applyClaimRiskFlagsPayload(payload)
      delete expenseAttachmentMeta[item.id]
      applyLocalExpenseItemPatch(item.id, {
        invoiceId: '',
        attachmentHint: resolveExpenseUploadHint()
      })
      if (editingExpenseId.value === item.id) {
        expenseEditor.invoiceId = ''
      }
      if (attachmentPreviewOpen.value) {
        closeAttachmentPreview()
      }
      emit('request-updated', { claimId: request.value.claimId })
      toast(payload?.message || '附件已删除。')
    } catch (error) {
      toast(error?.message || '附件删除失败，请稍后重试。')
    } finally {
      deletingAttachmentId.value = ''
    }
  }

  async function handleExpenseFileChange(event) {
    const target = event?.target
    const fileList = target?.files
    const fileCount = fileList?.length || 0
    const file = fileList?.[0]
    const itemId = pendingUploadExpenseId.value
    pendingUploadExpenseId.value = ''

    if (target) {
      target.value = ''
    }

    if (fileCount > 1) {
      toast('一条费用明细只能上传一张单据，请只选择一个文件。')
      return
    }

    if (!file || !itemId) {
      return
    }

    const item = expenseItems.value.find((entry) => entry.id === itemId)
    if (!item) {
      toast('未找到对应的费用明细，请刷新后重试。')
      return
    }

    await uploadExpenseFile(item, file)
  }

  async function removeExpenseItem(item) {
    if (!request.value.claimId || !item?.id || getActionBusy()) {
      return
    }
    if (item?.isSystemGenerated) {
      toast('系统自动计算的补贴行不能删除。')
      return
    }

    deletingExpenseId.value = item.id
    try {
      const payload = await deleteExpenseClaimItem(request.value.claimId, item.id)
      delete expenseAttachmentMeta[item.id]
      expenseItems.value = rebuildExpenseItems(
        expenseItems.value.filter((entry) => entry.id !== item.id),
        request.value
      )
      if (editingExpenseId.value === item.id) {
        resetExpenseEditorFields()
      }
      if (pendingUploadExpenseId.value === item.id) {
        pendingUploadExpenseId.value = ''
      }
      if (attachmentPreviewOpen.value) {
        closeAttachmentPreview()
      }
      emit('request-updated', { claimId: request.value.claimId })
      toast(payload?.message || '费用明细已删除。')
    } catch (error) {
      toast(error?.message || '费用明细删除失败，请稍后重试。')
    } finally {
      deletingExpenseId.value = ''
    }
  }

  async function saveExpenseEdit(item) {
    if (getActionBusy()) {
      toast(uploadingExpenseId.value ? '附件识别中，请等待识别完成后再保存。' : '当前操作处理中，请稍后再保存。')
      return
    }
    if (!request.value.claimId) {
      toast('当前草稿缺少 claimId，暂时无法保存费用明细。')
      return
    }

    const validationError = validateExpenseEditor()
    if (validationError) {
      toast(validationError)
      return
    }

    savingExpenseId.value = item.id
    try {
      const nextInvoiceId = expenseEditor.invoiceId.trim()
      const preservedLocation = String(item.itemLocation || expenseEditor.itemLocation || '').trim()
      const amountText = String(expenseEditor.itemAmount || '').trim()
      const nextAmount = amountText ? Number(amountText) : 0
      const itemPayload = {
        item_type: expenseEditor.itemType,
        item_reason: expenseEditor.itemReason.trim(),
        item_location: preservedLocation,
        item_note: expenseEditor.itemNote.trim(),
        item_amount: nextAmount,
        invoice_id: nextInvoiceId
      }
      if (expenseEditor.itemDate) {
        itemPayload.item_date = expenseEditor.itemDate
      }
      await updateExpenseClaimItem(request.value.claimId, item.id, itemPayload)
      applyLocalExpenseItemPatch(item.id, {
        itemDate: expenseEditor.itemDate || item.itemDate,
        itemType: expenseEditor.itemType,
        itemReason: expenseEditor.itemReason.trim(),
        itemLocation: preservedLocation,
        itemNote: expenseEditor.itemNote.trim(),
        itemAmount: nextAmount,
        invoiceId: nextInvoiceId
      })
      let riskNotice = ''
      if (nextInvoiceId) {
        try {
          const attachment = await refreshExpenseAttachmentMeta(item.id)
          riskNotice = buildAttachmentRiskNotice(attachment)
        } catch {
          delete expenseAttachmentMeta[item.id]
        }
      } else {
        delete expenseAttachmentMeta[item.id]
      }
      editingExpenseId.value = ''
      toast(riskNotice || '费用明细已保存。')
      emit('request-updated', { claimId: request.value.claimId })
    } catch (error) {
      toast(error?.message || '费用明细保存失败，请稍后重试。')
    } finally {
      savingExpenseId.value = ''
    }
  }

  function resetExpenseWorkState() {
    pendingUploadExpenseId.value = ''
    uploadingExpenseId.value = ''
    deletingExpenseId.value = ''
    editingExpenseId.value = ''
  }

  function disposeExpenseEditor() {
    if (stopSmartEntryRecognitionTask) {
      stopSmartEntryRecognitionTask()
      stopSmartEntryRecognitionTask = null
    }
  }

  return {
    applyLocalExpenseItemPatch,
    bindSmartEntryRecognitionTask,
    chooseSmartEntryFile,
    clearSmartEntryFile,
    closeSmartEntryUploadDialog,
    confirmSmartEntryUpload,
    deletingAttachmentId,
    deletingExpenseId,
    disposeExpenseEditor,
    editingExpenseId,
    expenseEditor,
    expenseTableColumnCount,
    expenseTotal,
    expenseUploadInput,
    handleExpenseFileChange,
    handleSmartEntryFileChange,
    hasExpenseRiskOrAbnormal,
    isMajorExpenseRisk,
    removeExpenseAttachment,
    removeExpenseItem,
    resetExpenseWorkState,
    resetSmartEntryRecognitionApplications,
    resolveExpenseIssues,
    resolveExpenseRiskIndicatorTitle,
    resolveExpenseRiskState,
    saveExpenseEdit,
    savingExpenseId,
    showExpenseRisk,
    smartEntryRecognitionBusy,
    smartEntryRecognitionText,
    smartEntrySelectedFileCount,
    smartEntrySelectedFileNames,
    smartEntrySelectedFileSummary,
    smartEntryUploadBusy,
    smartEntryUploadDialogOpen,
    smartEntryUploadInput,
    startExpenseEdit,
    triggerExpenseUpload,
    triggerSmartEntryUpload,
    uploadedExpenseCount,
    uploadingExpenseId
  }
}
