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

import {
  acceptExpenseClaimStandardAdjustment,
  calculateTravelReimbursement,
  preReviewExpenseClaim,
  submitExpenseClaim,
  updateExpenseClaim
} from '../../services/reimbursements.js'
import { filterRiskCardsForVisibility } from '../../utils/riskVisibility.js'
import {
  buildEmployeeProfileAdviceItems,
  buildTravelReceiptMaterialPrompts
} from './travelRequestDetailAdviceModel.js'
import {
  buildDraftBlockingIssues,
  mapIssueToAdvice,
  normalizeDetailNoteDraftValue,
  rebuildExpenseItems
} from './travelRequestDetailExpenseModel.js'
import {
  buildAiAdviceViewModel,
  buildAttachmentRiskCards,
  buildClaimSummaryRiskCards,
  filterRiskCardsByBusinessStage,
  extractRiskTagsFromText,
  normalizeRiskTone,
  resolveRiskTags
} from './travelRequestDetailInsights.js'
import { mapPreReviewFindingsToRiskFlags } from './travelRequestDetailPreReview.js'
import {
  buildCurrentStandardAdjustmentMap,
  buildStandardAdjustmentPayload as buildStandardAdjustmentPayloadModel,
  filterSubmitterResolvedRiskCards as filterSubmitterResolvedRiskCardsModel,
  isRiskCardMissingExpenseNote as isRiskCardMissingExpenseNoteModel,
  resolveExpenseItemsForRiskCard as resolveExpenseItemsForRiskCardModel
} from './travelRequestDetailStandardAdjustment.js'
import {
  resolveSubmitActionIcon,
  resolveSubmitActionLabel,
  resolveSubmitConfirmDescription,
  resolveSubmitConfirmText
} from './travelRequestDetailSubmitModel.js'
import { useTravelRequestEmployeeRiskProfile } from './useTravelRequestEmployeeRiskProfile.js'

export function useTravelRequestDetailRiskSubmit({
  request,
  expenseItems,
  expenseAttachmentMeta,
  riskFlagPreviewSnapshot,
  isApplicationDocument,
  isEditableRequest,
  isDraftRequest,
  isCurrentApplicant,
  canViewApprovalRiskAdvice,
  riskViewerContext,
  getActionBusy,
  toast,
  emit
}) {
  const submitBusy = ref(false)
  const standardAdjustmentBusy = ref(false)
  const submitConfirmDialogOpen = ref(false)
  const riskOverrideDialogOpen = ref(false)
  const riskOverrideBusy = ref(false)
  const riskOverrideIndex = ref(0)
  const highlightedRiskCardId = ref('')
  const detailNoteEditor = ref('')
  const savingDetailNote = ref(false)
  const { employeeRiskProfile } = useTravelRequestEmployeeRiskProfile({
    request,
    isApplicationDocument
  })
  let standardAdjustmentTaskSeq = 0
  let submitTaskSeq = 0
  let highlightedRiskCardTimer = 0

  const canEditDetailNote = computed(() => isDraftRequest.value)
  const detailNoteSource = computed(() => normalizeDetailNoteDraftValue(request.value.note))
  const detailNote = computed(() => {
    if (detailNoteSource.value) {
      return stripRiskTagsForDisplay(detailNoteSource.value)
    }
    return '暂无附加说明。请补充本次出差或办事事由，例如“去北京客户现场出差，拜访 XX 客户并处理项目验收事项”。'
  })
  const detailNoteEditorView = computed({
    get: () => stripRiskTagsForDisplay(detailNoteEditor.value),
    set: (value) => {
      detailNoteEditor.value = mergeVisibleNoteWithHiddenTags(value, detailNoteEditor.value)
    }
  })
  const detailNoteDirty = computed(() => detailNoteEditor.value.trim() !== detailNoteSource.value)
  const detailNoteTags = computed(() =>
    extractRiskTagsFromText(canEditDetailNote.value ? detailNoteEditor.value : detailNoteSource.value)
  )
  const draftBlockingIssues = computed(() =>
    isEditableRequest.value ? buildDraftBlockingIssues(request.value, expenseItems.value) : []
  )
  const canSubmit = computed(() => isEditableRequest.value && !getActionBusy())

  watch(
    () => [request.value.claimId, detailNoteSource.value],
    ([, nextNote]) => {
      detailNoteEditor.value = nextNote
    },
    { immediate: true }
  )

  function stripRiskTagsForDisplay(value) {
    return String(value || '')
      .split('\n')
      .map((line) =>
        line
          .replace(/(?:^|\s)#[A-Za-z_]+(?=\s|$)/g, ' ')
          .replace(/[ \t]{2,}/g, ' ')
          .replace(/:\s+第/g, ':第')
          .trim()
      )
      .join('\n')
      .trim()
  }

  function mergeVisibleNoteWithHiddenTags(visibleText, rawText) {
    const cleanText = normalizeDetailNoteDraftValue(visibleText)
    const tags = extractRiskTagsFromText(rawText).join(' ')
    return [cleanText, tags].map((item) => String(item || '').trim()).filter(Boolean).join('\n')
  }

  function resolveClaimRiskFlags() {
    const flags = request.value?.riskFlags || request.value?.risk_flags_json || []
    let requestFlags = Array.isArray(flags) ? flags : []
    const previewSnapshot = riskFlagPreviewSnapshot.value
    if (
      previewSnapshot
      && previewSnapshot.claimId === request.value?.claimId
      && Array.isArray(previewSnapshot.riskFlags)
    ) {
      requestFlags = previewSnapshot.riskFlags
    }
    return requestFlags
  }

  function applyClaimRiskFlagsPayload(payload) {
    const flags = [
      payload?.claim_risk_flags,
      payload?.claimRiskFlags,
      payload?.risk_flags_json,
      payload?.riskFlags
    ].find(Array.isArray) || (
      Array.isArray(payload?.pre_review?.findings)
        ? mapPreReviewFindingsToRiskFlags(payload.pre_review)
        : null
    )
    if (!flags) {
      return
    }
    riskFlagPreviewSnapshot.value = {
      claimId: request.value.claimId,
      riskFlags: flags
    }
  }

  function clearRiskFlagPreviewSnapshot() {
    riskFlagPreviewSnapshot.value = null
  }

  function resolveCurrentStandardAdjustmentMap() {
    return buildCurrentStandardAdjustmentMap(request.value, resolveClaimRiskFlags())
  }

  function resolveExpenseItemsForRiskCard(card) {
    return resolveExpenseItemsForRiskCardModel(card, expenseItems.value)
  }

  function filterSubmitterResolvedRiskCards(cards, businessStage) {
    const viewerContext = riskViewerContext.value || {}
    return filterSubmitterResolvedRiskCardsModel({
      cards,
      businessStage,
      isCurrentApplicant: isCurrentApplicant.value,
      isPrivilegedRiskViewer: Boolean(
        viewerContext.isAdminViewer
        || viewerContext.isBudgetReviewer
        || viewerContext.isDirectManagerReviewer
        || viewerContext.isFinanceReviewer
        || viewerContext.canViewApprovalRiskAdvice
      ),
      expenseItems: expenseItems.value,
      standardAdjustmentMap: resolveCurrentStandardAdjustmentMap()
    })
  }

  function isRiskCardMissingExpenseNote(card) {
    return isRiskCardMissingExpenseNoteModel(card, expenseItems.value)
  }

  function resolveRiskWarningNotes(card) {
    const notes = resolveExpenseItemsForRiskCard(card)
      .map((item) => String(item?.itemNote || '').trim())
      .filter(Boolean)
    return [...new Set(notes)]
  }

  async function buildStandardAdjustmentPayload() {
    return buildStandardAdjustmentPayloadModel({
      warnings: submitRiskCards.value,
      expenseItems: expenseItems.value,
      request: request.value,
      calculateTravelReimbursement
    })
  }

  function applyStandardAdjustmentResponse(payload = {}) {
    const flags = Array.isArray(payload?.risk_flags_json)
      ? payload.risk_flags_json
      : Array.isArray(payload?.riskFlags)
        ? payload.riskFlags
        : resolveClaimRiskFlags()
    riskFlagPreviewSnapshot.value = {
      claimId: request.value.claimId,
      riskFlags: flags
    }
    const sourceItems = Array.isArray(payload?.items) && payload.items.length
      ? payload.items
      : expenseItems.value
    expenseItems.value = rebuildExpenseItems(sourceItems, {
      ...request.value,
      riskFlags: flags,
      risk_flags_json: flags
    })
  }

  const aiAdvice = computed(() => {
    const completionItems = isEditableRequest.value
      ? draftBlockingIssues.value.map(mapIssueToAdvice).filter(Boolean)
      : []
    const currentBusinessStage = isApplicationDocument.value ? 'expense_application' : 'reimbursement'
    const directRiskCards = filterRiskCardsByBusinessStage(
      buildAttachmentRiskCards({
        expenseItems: expenseItems.value,
        attachmentMetaByItemId: expenseAttachmentMeta,
        claimRiskFlags: resolveClaimRiskFlags(),
        businessStage: currentBusinessStage
      }),
      currentBusinessStage
    )
    const hasActionableRiskCards = directRiskCards.some(
      (card) => ['medium', 'high'].includes(normalizeRiskTone(card?.tone))
    )
    const summaryRiskCards = filterRiskCardsByBusinessStage(
      buildClaimSummaryRiskCards({
        ...(request.value || {}),
        businessStage: currentBusinessStage
      }),
      currentBusinessStage
    )
    const materialPrompts = currentBusinessStage === 'reimbursement'
      ? buildTravelReceiptMaterialPrompts(request.value, expenseItems.value)
      : []
    const profileAdviceItems = currentBusinessStage === 'reimbursement'
      ? buildEmployeeProfileAdviceItems(employeeRiskProfile.value)
      : []
    const scopedRiskCards = [
      ...(hasActionableRiskCards ? [] : summaryRiskCards),
      ...filterSubmitterResolvedRiskCards(directRiskCards, currentBusinessStage)
    ]
    const riskCards = filterRiskCardsForVisibility(scopedRiskCards, riskViewerContext.value)

    return buildAiAdviceViewModel({
      completionItems,
      materialPrompts,
      profileAdviceItems,
      riskCards
    })
  })

  const hasVisibleRiskCards = computed(() =>
    aiAdvice.value.riskCards.some((card) => ['medium', 'high'].includes(normalizeRiskTone(card?.tone)))
  )
  const hasAdviceSections = computed(() => aiAdvice.value.sections.length > 0)
  const showCompactSafeAdvice = computed(() =>
    isEditableRequest.value
    && !isApplicationDocument.value
    && !draftBlockingIssues.value.length
  )
  const showAiAdvicePanel = computed(() => (
    (
      isEditableRequest.value
      && (
        hasAdviceSections.value
        || showCompactSafeAdvice.value
      )
    )
    || (!isEditableRequest.value && canViewApprovalRiskAdvice.value && aiAdvice.value.riskCards.length > 0)
    || (!isEditableRequest.value && isCurrentApplicant.value && hasVisibleRiskCards.value)
  ))

  function normalizeRiskDomId(value) {
    return String(value || '').trim().replace(/[^A-Za-z0-9_-]/g, '-') || 'unknown'
  }

  function resolveRiskCardDomId(card) {
    return `detail-risk-card-${normalizeRiskDomId(card?.id)}`
  }

  function isHighlightedRiskCard(card) {
    return Boolean(card?.id) && String(card.id) === highlightedRiskCardId.value
  }

  function resolveExpenseRiskTargetCard(item) {
    const itemId = String(item?.id || '').trim()
    const invoiceId = String(item?.invoiceId || '').trim()
    const itemIndex = expenseItems.value.findIndex((entry) => entry.id === item?.id) + 1
    const cards = Array.isArray(aiAdvice.value?.riskCards) ? aiAdvice.value.riskCards : []
    const actionableCards = cards.filter((card) => ['medium', 'high'].includes(normalizeRiskTone(card?.tone)))

    return actionableCards.find((card) => {
      const cardItemIds = [
        card?.itemId,
        card?.item_id,
        ...(Array.isArray(card?.itemIds) ? card.itemIds : []),
        ...(Array.isArray(card?.item_ids) ? card.item_ids : [])
      ].map((value) => String(value || '').trim()).filter(Boolean)
      return cardItemIds.includes(itemId)
    })
      || actionableCards.find((card) => invoiceId && String(card?.invoiceId || card?.invoice_id || '').trim() === invoiceId)
      || actionableCards.find((card) => Number(card?.itemIndex || card?.item_index || 0) === itemIndex)
      || actionableCards.find((card) => itemIndex > 0 && String(card?.title || '').includes(`第 ${itemIndex} 条`))
      || null
  }

  function hasExpenseRiskIndicator(item) {
    return Boolean(resolveExpenseRiskTargetCard(item))
  }

  async function focusExpenseRisk(item) {
    const card = resolveExpenseRiskTargetCard(item)
    const riskSection = document.querySelector('.validation-section--risk')
    if (!card && !riskSection) {
      toast('当前费用明细暂无可定位的风险点。')
      return
    }

    highlightedRiskCardId.value = card?.id ? String(card.id) : ''
    await nextTick()

    const target = card
      ? document.getElementById(resolveRiskCardDomId(card))
      : riskSection
    target?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })

    if (highlightedRiskCardTimer) {
      window.clearTimeout(highlightedRiskCardTimer)
    }
    highlightedRiskCardTimer = window.setTimeout(() => {
      highlightedRiskCardId.value = ''
      highlightedRiskCardTimer = 0
    }, 1800)
  }

  const aiAdviceTitle = computed(() => {
    if (!isEditableRequest.value && isCurrentApplicant.value) {
      return isApplicationDocument.value ? '申请风险提示' : '风险提示'
    }
    if (isEditableRequest.value && isApplicationDocument.value) {
      return '表单自查提示'
    }
    return isEditableRequest.value ? 'AI建议' : '风险提示'
  })
  const aiAdviceHint = computed(() => {
    if (!isEditableRequest.value && isCurrentApplicant.value) {
      return isApplicationDocument.value
        ? '展示申请单已识别的风险点及原因，请逐条确认或补充说明后再提交给领导审批。'
        : '展示票据、行程、金额等可自行修正的风险点，便于提交人先整改，减少后续退单。'
    }
    return isEditableRequest.value
      ? (isApplicationDocument.value ? '仅提示申请表单本身需要补充的内容，不展示预算治理细节。' : '系统会在草稿保存和附件识别后自动更新检测结果。')
      : '展示系统已识别的风险点，便于审批和后续整改。'
  })

  const submitActionLabel = computed(() => resolveSubmitActionLabel({
    isApplicationDocument: isApplicationDocument.value,
    submitBusy: submitBusy.value
  }))
  const submitActionIcon = computed(() => resolveSubmitActionIcon({
    isApplicationDocument: isApplicationDocument.value
  }))
  const submitConfirmDescription = computed(() => resolveSubmitConfirmDescription({
    isApplicationDocument: isApplicationDocument.value,
    hasHighRiskWarnings: aiAdvice.value.riskCards.some((card) => ['medium', 'high'].includes(normalizeRiskTone(card?.tone)))
  }))
  const submitConfirmText = computed(() => resolveSubmitConfirmText(isApplicationDocument.value))
  const submitRiskCards = computed(() =>
    aiAdvice.value.riskCards
      .filter((card) => ['medium', 'high'].includes(normalizeRiskTone(card?.tone)))
      .map((card, index) => ({
        ...card,
        id: String(card.id || `submit-risk-${index}`),
        tags: resolveRiskTags(card)
      }))
  )
  const submitConfirmSecondaryText = computed(() => (
    !isApplicationDocument.value && submitRiskCards.value.length
      ? '按职级标准报销'
      : ''
  ))
  const submitRiskWarnings = computed(() =>
    submitRiskCards.value.filter((card) => isRiskCardMissingExpenseNote(card))
  )
  const submitExplainedRiskWarnings = computed(() =>
    submitRiskCards.value.filter((card) => !isRiskCardMissingExpenseNote(card))
  )
  const hasMissingSubmitRiskWarnings = computed(() => submitRiskWarnings.value.length > 0)
  const submitRiskReviewWarnings = computed(() =>
    hasMissingSubmitRiskWarnings.value ? submitRiskWarnings.value : submitExplainedRiskWarnings.value
  )
  const currentSubmitRiskWarning = computed(() => submitRiskReviewWarnings.value[riskOverrideIndex.value] || null)
  const currentSubmitRiskWarningNotes = computed(() =>
    currentSubmitRiskWarning.value ? resolveRiskWarningNotes(currentSubmitRiskWarning.value) : []
  )
  const riskOverrideIndexLabel = computed(() =>
    submitRiskReviewWarnings.value.length ? `${riskOverrideIndex.value + 1} / ${submitRiskReviewWarnings.value.length}` : ''
  )
  const riskOverrideBadgeTone = computed(() => hasMissingSubmitRiskWarnings.value ? 'danger' : 'warning')
  const riskOverrideDialogTitle = computed(() => (
    hasMissingSubmitRiskWarnings.value
      ? `当前存在 ${submitRiskWarnings.value.length} 条需说明的风险`
      : `请确认 ${submitExplainedRiskWarnings.value.length} 条风险及异常说明`
  ))
  const riskOverrideDialogDescription = computed(() => (
    hasMissingSubmitRiskWarnings.value
      ? '请回到费用明细的异常说明列补充原因后再提交；如果不补充说明，可选择按职级最高可报销金额重新计算。'
      : '请核对风险点与已填写的异常说明，确认后进入提交确认。'
  ))
  const riskOverrideCancelText = computed(() => (
    hasMissingSubmitRiskWarnings.value ? '返回整改' : '返回核对'
  ))
  const riskOverrideConfirmText = computed(() =>
    hasMissingSubmitRiskWarnings.value ? '按职级标准重算' : '确认说明'
  )
  const riskOverrideConfirmTone = computed(() => hasMissingSubmitRiskWarnings.value ? 'danger' : 'primary')
  const riskOverrideConfirmIcon = computed(() =>
    hasMissingSubmitRiskWarnings.value ? 'mdi mdi-calculator-variant-outline' : 'mdi mdi-check-circle-outline'
  )
  const riskOverrideGuidanceTitle = computed(() => (
    hasMissingSubmitRiskWarnings.value
      ? '请在费用明细的“异常说明”列补充原因后再提交。'
      : '已填写异常说明，请确认说明会随单据进入审批。'
  ))
  const riskOverrideGuidanceText = computed(() => (
    hasMissingSubmitRiskWarnings.value
      ? '如果不补充说明，可直接选择按职级标准重算，超出标准的部分由员工自担。'
      : '确认后系统会继续进入提交确认，领导和财务可看到这些风险及对应说明。'
  ))

  const approvalRiskConfirmItems = computed(() =>
    aiAdvice.value.riskCards
      .filter((card) => ['medium', 'high'].includes(normalizeRiskTone(card?.tone)))
      .slice(0, 4)
      .map((card, index) => ({
        id: String(card?.id || `approval-risk-${index + 1}`),
        tone: normalizeRiskTone(card?.tone),
        label: normalizeRiskTone(card?.tone) === 'high' ? '高风险' : '中风险',
        title: String(card?.title || card?.label || '风险提示').trim(),
        description: String(
          card?.relatedExplanationSummary
          || card?.risk
          || card?.summary
          || card?.suggestion
          || '请核对该风险点对应的说明和佐证材料。'
        ).trim()
      }))
  )

  function resetDetailNote() {
    detailNoteEditor.value = detailNoteSource.value
  }

  async function saveDetailNote() {
    if (!canEditDetailNote.value || savingDetailNote.value) {
      return
    }
    if (!request.value.claimId) {
      toast('当前草稿缺少 claimId，暂时无法保存附加说明。')
      return
    }
    if (!detailNoteDirty.value) {
      return
    }

    savingDetailNote.value = true
    try {
      await updateExpenseClaim(request.value.claimId, {
        reason: detailNoteEditor.value.trim()
      })
      toast('附加说明已保存。')
      emit('request-updated', { claimId: request.value.claimId })
    } catch (error) {
      toast(error?.message || '附加说明保存失败，请稍后重试。')
    } finally {
      savingDetailNote.value = false
    }
  }

  function openRiskOverrideDialog() {
    const warnings = submitRiskReviewWarnings.value
    if (!warnings.length) {
      return
    }
    riskOverrideIndex.value = 0
    riskOverrideDialogOpen.value = true
  }

  function closeRiskOverrideDialog() {
    if (riskOverrideBusy.value) {
      return
    }
    riskOverrideDialogOpen.value = false
  }

  function goToPreviousSubmitRisk() {
    if (!submitRiskReviewWarnings.value.length) {
      return
    }
    riskOverrideIndex.value =
      (riskOverrideIndex.value - 1 + submitRiskReviewWarnings.value.length) % submitRiskReviewWarnings.value.length
  }

  function goToNextSubmitRisk() {
    if (!submitRiskReviewWarnings.value.length) {
      return
    }
    riskOverrideIndex.value = (riskOverrideIndex.value + 1) % submitRiskReviewWarnings.value.length
  }

  function confirmRiskExplanation() {
    if (riskOverrideBusy.value || submitBusy.value) {
      return
    }
    riskOverrideDialogOpen.value = false
    submitConfirmDialogOpen.value = true
  }

  function confirmRiskOverrideDialog() {
    if (hasMissingSubmitRiskWarnings.value) {
      confirmStandardAdjustment()
      return
    }
    confirmRiskExplanation()
  }

  function confirmStandardAdjustment() {
    if (riskOverrideBusy.value || standardAdjustmentBusy.value) {
      return
    }
    const claimId = String(request.value?.claimId || '').trim()
    if (!claimId) {
      toast('当前草稿缺少 claimId，暂时无法按职级标准重算。')
      return
    }

    riskOverrideDialogOpen.value = false
    submitConfirmDialogOpen.value = false
    standardAdjustmentBusy.value = true
    const taskSeq = ++standardAdjustmentTaskSeq
    toast('正在后台按职级标准重新测算费用。')
    void runStandardAdjustmentRecalculation(claimId, taskSeq)
  }

  async function runStandardAdjustmentRecalculation(claimId, taskSeq) {
    try {
      const payload = await buildStandardAdjustmentPayload()
      if (!payload.risks.length) {
        toast('当前风险暂未匹配到可重算的费用明细，请先补充异常说明。')
        return
      }
      const response = await acceptExpenseClaimStandardAdjustment(claimId, payload)
      if (taskSeq !== standardAdjustmentTaskSeq || String(request.value?.claimId || '').trim() !== claimId) {
        return
      }
      applyStandardAdjustmentResponse(response)
      toast('已按职级最高报销标准重算实际报销金额，可继续提交审批。')
      emit('request-updated', { claimId: request.value.claimId })
    } catch (error) {
      toast(error?.message || '按职级标准重算失败，请稍后重试。')
    } finally {
      if (taskSeq === standardAdjustmentTaskSeq) {
        standardAdjustmentBusy.value = false
      }
    }
  }

  async function handleSubmit() {
    if (!request.value.claimId) {
      toast('当前草稿缺少 claimId，暂时无法提交。')
      return
    }

    if (!canSubmit.value) {
      toast('当前单据正在保存或处理附件，请稍后再提交审批。')
      return
    }

    if (standardAdjustmentBusy.value) {
      toast('费用正在按职级标准重新测算，完成后再提交审批。')
      return
    }

    if (draftBlockingIssues.value.length) {
      toast(draftBlockingIssues.value[0] || '请先补全草稿信息，再提交审批。')
      return
    }

    if (submitRiskReviewWarnings.value.length) {
      openRiskOverrideDialog()
      return
    }

    submitConfirmDialogOpen.value = true
  }

  function closeSubmitConfirmDialog() {
    if (submitBusy.value) {
      return
    }

    submitConfirmDialogOpen.value = false
  }

  function confirmSubmitRequest() {
    if (!request.value.claimId) {
      toast('当前草稿缺少 claimId，暂时无法提交。')
      submitConfirmDialogOpen.value = false
      return
    }

    if (!canSubmit.value) {
      toast('当前单据正在保存或处理附件，请稍后再提交审批。')
      submitConfirmDialogOpen.value = false
      return
    }

    if (standardAdjustmentBusy.value) {
      toast('费用正在按职级标准重新测算，完成后再提交审批。')
      submitConfirmDialogOpen.value = false
      return
    }

    if (draftBlockingIssues.value.length) {
      toast(draftBlockingIssues.value[0] || '请先补全草稿信息，再提交审批。')
      submitConfirmDialogOpen.value = false
      return
    }

    const claimId = String(request.value.claimId || '').trim()
    const documentNo = request.value.id
    const isApplication = isApplicationDocument.value
    submitBusy.value = true
    submitConfirmDialogOpen.value = false
    const taskSeq = ++submitTaskSeq
    toast('正在后台提交审批，完成后会自动更新单据状态。')
    void runSubmitRequest(claimId, documentNo, isApplication, taskSeq)
  }

  async function runSubmitRequest(claimId, documentNo, isApplication, taskSeq) {
    try {
      const reviewedClaim = await preReviewExpenseClaim(claimId)
      if (taskSeq !== submitTaskSeq || String(request.value?.claimId || '').trim() !== claimId) {
        return
      }
      applyClaimRiskFlagsPayload(reviewedClaim)
      const preReview = reviewedClaim?.pre_review || reviewedClaim?.preReview || null
      if (String(preReview?.decision || '').trim() === 'needs_fix') {
        toast(preReview?.message || '自动检测发现需要先整改的风险，请处理后重新提交。')
        emit('request-updated', { claimId })
        return
      }

      const payload = await submitExpenseClaim(claimId, {
        pre_review_id: String(preReview?.review_id || preReview?.reviewId || '').trim() || null,
        input_fingerprint: String(
          preReview?.input_fingerprint || preReview?.inputFingerprint || ''
        ).trim() || null
      })
      if (taskSeq !== submitTaskSeq || String(request.value?.claimId || '').trim() !== claimId) {
        return
      }
      const claimStatus = String(payload?.status || '').trim().toLowerCase()
      const approvalStage = String(payload?.approval_stage || payload?.approvalStage || '').trim()
      if (claimStatus === 'submitted') {
        toast(
          isApplication
            ? `${documentNo} 申请单已提交${approvalStage ? `，当前节点：${approvalStage}` : '，等待直属领导审批'}。`
            : `${documentNo} 已提交审批${approvalStage ? `，当前节点：${approvalStage}` : '。'}`
        )
      } else if (claimStatus === 'supplement') {
        toast(`${documentNo} 自动检测未通过，已转待补充。`)
      } else {
        toast(`${documentNo} 提交结果已更新。`)
      }
      submitConfirmDialogOpen.value = false
      emit('request-updated', { claimId })
    } catch (error) {
      if (taskSeq === submitTaskSeq) {
        if (['PRE_REVIEW_NEEDS_FIX', 'PRE_REVIEW_CHANGED'].includes(error?.code)) {
          applyClaimRiskFlagsPayload({ pre_review: error?.review })
          toast(error?.review?.message || error?.message || '自动检测发现需要先整改的风险。')
          emit('request-updated', { claimId })
          return
        }
        toast(error?.message || '提交审批失败，请稍后重试。')
      }
    } finally {
      if (taskSeq === submitTaskSeq) {
        submitBusy.value = false
      }
    }
  }

  function resetSubmitWorkState() {
    standardAdjustmentTaskSeq += 1
    standardAdjustmentBusy.value = false
    submitTaskSeq += 1
    submitBusy.value = false
  }
  function disposeRiskSubmit() {
    resetSubmitWorkState()
    if (highlightedRiskCardTimer) {
      window.clearTimeout(highlightedRiskCardTimer)
      highlightedRiskCardTimer = 0
    }
  }

  return {
    aiAdvice,
    aiAdviceHint,
    aiAdviceTitle,
    applyClaimRiskFlagsPayload,
    approvalRiskConfirmItems,
    canEditDetailNote,
    canSubmit,
    clearRiskFlagPreviewSnapshot,
    closeRiskOverrideDialog,
    closeSubmitConfirmDialog,
    confirmRiskExplanation,
    confirmRiskOverrideDialog,
    confirmStandardAdjustment,
    confirmSubmitRequest,
    currentSubmitRiskWarning,
    currentSubmitRiskWarningNotes,
    detailNote,
    detailNoteDirty,
    detailNoteEditor,
    detailNoteEditorView,
    detailNoteTags,
    disposeRiskSubmit,
    draftBlockingIssues,
    focusExpenseRisk,
    goToNextSubmitRisk,
    goToPreviousSubmitRisk,
    handleSubmit,
    hasExpenseRiskIndicator,
    hasMissingSubmitRiskWarnings,
    isHighlightedRiskCard,
    resetDetailNote,
    resetSubmitWorkState,
    resolveClaimRiskFlags,
    resolveRiskCardDomId,
    riskOverrideBadgeTone,
    riskOverrideBusy,
    riskOverrideCancelText,
    riskOverrideConfirmIcon,
    riskOverrideConfirmText,
    riskOverrideConfirmTone,
    riskOverrideDialogDescription,
    riskOverrideDialogOpen,
    riskOverrideDialogTitle,
    riskOverrideGuidanceText,
    riskOverrideGuidanceTitle,
    riskOverrideIndexLabel,
    saveDetailNote,
    savingDetailNote,
    showAiAdvicePanel,
    standardAdjustmentBusy,
    submitActionIcon,
    submitActionLabel,
    submitBusy,
    submitConfirmDescription,
    submitConfirmDialogOpen,
    submitConfirmSecondaryText,
    submitConfirmText,
    submitExplainedRiskWarnings,
    submitRiskReviewWarnings,
    submitRiskWarnings
  }
}
