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

import { useSystemState } from '../../composables/useSystemState.js'
import { useToast } from '../../composables/useToast.js'
import {
  AI_APPLICATION_ACTION_SAVE_DRAFT,
  runAiApplicationPreviewAction
} from '../../services/aiApplicationPreviewActions.js'
import { calculateTravelReimbursement } from '../../services/reimbursements.js'
import {
  canApproveBudgetExpenseApplications,
  canApproveLeaderExpenseClaims,
  canManageExpenseClaims,
  canReturnExpenseClaims,
  isCurrentDirectManagerForRequest,
  isCurrentRequestApplicant,
  isFinanceUser,
  isPlatformAdminUser
} from '../../utils/accessControl.js'
import {
  buildLeaderApprovalEvents,
  buildLeaderApprovalInfo
} from '../../utils/applicationApproval.js'
import {
  buildApplicationDetailFactItems,
  buildRelatedApplicationFactItems
} from '../../utils/expenseApplicationDetail.js'
import { buildRiskViewerContext } from '../../utils/riskVisibility.js'
import { resolveProgressStepsForViewer } from '../../utils/requestProgressViewer.js'
import { isArchivedRequestView, normalizeRequestForUi } from '../../utils/requestViewModel.js'
import {
  APPLICATION_TRANSPORT_MODE_OPTIONS,
  applyApplicationPolicyEstimateError,
  applyApplicationPolicyEstimateResult,
  buildApplicationPolicyEstimateRequest,
  normalizeApplicationPreview
} from '../../utils/expenseApplicationPreview.js'
import {
  EXPENSE_TYPE_OPTIONS,
  buildFallbackExpenseItems,
  buildFallbackProgressSteps,
  isApplicationDocumentRequest,
  rebuildExpenseItems,
  resolveExpenseReasonHelper,
  resolveExpenseReasonPlaceholder
} from './travelRequestDetailExpenseModel.js'
import { useTravelRequestPaymentFlow } from './travelRequestDetailPaymentFlow.js'
import { useTravelRequestDetailApprovalFlow } from './useTravelRequestDetailApprovalFlow.js'
import { useTravelRequestDetailAttachmentPreview } from './useTravelRequestDetailAttachmentPreview.js'
import { useTravelRequestDetailExpenseEditor } from './useTravelRequestDetailExpenseEditor.js'
import { useTravelRequestDetailRiskSubmit } from './useTravelRequestDetailRiskSubmit.js'

export function useTravelRequestDetailSetup(props, { emit }) {
  const { toast } = useToast()
  const { currentUser } = useSystemState()
  const expenseItems = ref([])
  const expenseAttachmentMeta = reactive({})
  const applicationDetailEditor = reactive({
    fieldKey: '',
    draftValue: '',
    saving: false
  })
  const riskFlagPreviewSnapshot = ref(null)
  let actionBusy = { value: false }
  const getActionBusy = () => Boolean(actionBusy?.value)

  const request = computed(() => {
    const normalized = normalizeRequestForUi(props.request)

    return (
      normalized || {
        id: 'EXP-202605-000',
        claimId: '',
        reason: '待补充报销事由',
        typeLabel: '其他费用',
        typeCode: 'other',
        detailVariant: 'general',
        sceneTarget: '待补充',
        location: '待补充',
        occurredDisplay: '待补充',
        applyTime: '待补充',
        amountDisplay: '￥0',
        amountValue: 0,
        node: '待提交',
        approval: '草稿',
        approvalKey: 'draft',
        approvalTone: 'draft',
        secondaryStatusLabel: '票据状态',
        secondaryStatusValue: '待补充',
        secondaryStatusTone: 'warning',
        relatedCustomer: '待补充',
        attachmentSummary: '待补充',
        riskSummary: '待补充',
        note: '',
        profileIdentity: '员工',
        profilePosition: '待补充',
        profileGrade: '待补充',
        profileManager: '待补充',
        profileName: '当前申请人',
        profileDepartment: '待补充部门',
        profileAvatar: '申'
      }
    )
  })

  const isApplicationDocument = computed(() => isApplicationDocumentRequest(request.value))
  const isTravelRequest = computed(() => request.value.detailVariant === 'travel' && !isApplicationDocument.value)
  const isDraftRequest = computed(() => request.value.approvalKey === 'draft')
  const isEditableRequest = computed(() => ['draft', 'supplement'].includes(request.value.approvalKey))
  const canOpenAiEntry = computed(() => isEditableRequest.value)
  const isCurrentApplicant = computed(() => isCurrentRequestApplicant(request.value, currentUser.value))
  const canModifyApplication = computed(() => (
    isApplicationDocument.value
    && isEditableRequest.value
    && isCurrentApplicant.value
  ))
  const canManageCurrentClaim = computed(() => canManageExpenseClaims(currentUser.value))
  const isArchivedRequest = computed(() => isArchivedRequestView(request.value))
  const isApplicantDeletableRequest = computed(() => {
    if (!isCurrentApplicant.value) {
      return false
    }
    const status = String(request.value.status || request.value.approvalKey || '').trim().toLowerCase()
    return ['draft', 'supplement', 'returned'].includes(status)
  })
  const canDeleteRequest = computed(() => {
    if (isPlatformAdminUser(currentUser.value)) {
      return true
    }
    return isApplicantDeletableRequest.value
  })
  const isDirectManagerApprovalStage = computed(() => {
    const node = String(request.value.node || request.value.approvalStage || '').trim()
    return node === '直属领导审批'
  })
  const isFinanceApprovalStage = computed(() => {
    const node = String(request.value.node || request.value.approvalStage || '').trim()
    return node === '财务审批'
  })
  const isBudgetApprovalStage = computed(() => {
    const node = String(request.value.node || request.value.approvalStage || '').trim()
    return node === '预算管理者审批'
  })
  const isCurrentDirectManagerApprover = computed(() => (
    canApproveLeaderExpenseClaims(currentUser.value)
    && isCurrentDirectManagerForRequest(request.value, currentUser.value)
  ))
  const canProcessFinanceApprovalStage = computed(() => (
    !isApplicationDocument.value
    && isFinanceApprovalStage.value
    && isFinanceUser(currentUser.value)
    && !isCurrentApplicant.value
  ))
  const canProcessBudgetApprovalStage = computed(() => (
    isApplicationDocument.value
    && isBudgetApprovalStage.value
    && canApproveBudgetExpenseApplications(currentUser.value, request.value)
    && !isCurrentApplicant.value
  ))
  const showBudgetAnalysis = computed(() => (
    isApplicationDocument.value
    && isBudgetApprovalStage.value
    && canApproveBudgetExpenseApplications(currentUser.value, request.value)
    && !isCurrentApplicant.value
  ))
  const canProcessCurrentApprovalStage = computed(() => {
    if (isDirectManagerApprovalStage.value) {
      return isCurrentDirectManagerApprover.value
    }
    if (isBudgetApprovalStage.value) {
      return canProcessBudgetApprovalStage.value
    }
    return canProcessFinanceApprovalStage.value
  })
  const canReturnRequest = computed(() => {
    if (request.value.approvalKey !== 'in_progress' || !request.value.claimId || !canReturnExpenseClaims(currentUser.value)) {
      return false
    }
    return canProcessCurrentApprovalStage.value
  })
  const canApproveRequest = computed(() =>
    request.value.approvalKey === 'in_progress'
    && Boolean(request.value.claimId)
    && canProcessCurrentApprovalStage.value
  )
  const canViewApprovalRiskAdvice = computed(() => (
    Boolean(request.value.claimId)
    && !isDraftRequest.value
    && !isCurrentApplicant.value
    && (canReturnRequest.value || canApproveRequest.value)
  ))
  const showStageRiskAdvice = computed(() => canViewApprovalRiskAdvice.value)
  const riskViewerContext = computed(() => buildRiskViewerContext({
    request: request.value,
    currentUser: currentUser.value,
    businessStage: isApplicationDocument.value ? 'expense_application' : 'reimbursement',
    isApplicationDocument: isApplicationDocument.value,
    isCurrentApplicant: isCurrentApplicant.value,
    isBudgetReviewer: canProcessBudgetApprovalStage.value,
    isDirectManagerReviewer: isCurrentDirectManagerApprover.value,
    isFinanceReviewer: canProcessFinanceApprovalStage.value,
    isAdminViewer: canManageCurrentClaim.value,
    canViewApprovalRiskAdvice: canViewApprovalRiskAdvice.value
  }))

  const paymentFlow = useTravelRequestPaymentFlow({
    request,
    currentUser,
    isApplicationDocument,
    isCurrentApplicant,
    toast,
    emit
  })
  const attachmentPreview = useTravelRequestDetailAttachmentPreview({
    request,
    expenseItems,
    expenseAttachmentMeta
  })
  const riskSubmit = useTravelRequestDetailRiskSubmit({
    request,
    expenseItems,
    expenseAttachmentMeta,
    riskFlagPreviewSnapshot,
    isApplicationDocument,
    isEditableRequest,
    isDraftRequest,
    isCurrentApplicant,
    canViewApprovalRiskAdvice,
    riskViewerContext,
    getActionBusy,
    toast,
    emit
  })
  const approvalFlow = useTravelRequestDetailApprovalFlow({
    request,
    isApplicationDocument,
    isDraftRequest,
    isArchivedRequest,
    isFinanceApprovalStage,
    isBudgetApprovalStage,
    canDeleteRequest,
    canReturnRequest,
    canApproveRequest,
    approvalRiskConfirmItems: riskSubmit.approvalRiskConfirmItems,
    canViewApprovalRiskAdvice,
    toast,
    emit
  })
  const expenseEditor = useTravelRequestDetailExpenseEditor({
    request,
    expenseItems,
    expenseAttachmentMeta,
    isEditableRequest,
    getActionBusy,
    toast,
    emit,
    attachmentPreviewOpen: attachmentPreview.attachmentPreviewOpen,
    buildAttachmentRiskNotice: attachmentPreview.buildAttachmentRiskNotice,
    closeAttachmentPreview: attachmentPreview.closeAttachmentPreview,
    refreshExpenseAttachmentMeta: attachmentPreview.refreshExpenseAttachmentMeta,
    resolveAttachmentMeta: attachmentPreview.resolveAttachmentMeta,
    resolveClaimRiskFlags: riskSubmit.resolveClaimRiskFlags,
    applyClaimRiskFlagsPayload: riskSubmit.applyClaimRiskFlagsPayload
  })
  const {
    deletingAttachmentId,
    deletingExpenseId,
    savingExpenseId,
    smartEntryRecognitionBusy,
    uploadingExpenseId
  } = expenseEditor

  actionBusy = computed(() =>
    Boolean(savingExpenseId.value)
    || riskSubmit.submitBusy.value
    || approvalFlow.deleteBusy.value
    || approvalFlow.returnBusy.value
    || approvalFlow.approveBusy.value
    || paymentFlow.payBusy.value
    || applicationDetailEditor.saving
    || smartEntryRecognitionBusy.value
    || Boolean(uploadingExpenseId.value)
    || Boolean(deletingAttachmentId.value)
    || Boolean(deletingExpenseId.value)
  )

  const profile = computed(() => ({
    name: request.value.profileName,
    identity: request.value.profileIdentity,
    position: request.value.profilePosition,
    department: request.value.profileDepartment,
    grade: request.value.profileGrade,
    manager: request.value.profileManager,
    avatar: request.value.profileAvatar
  }))
  const leaderApprovalInfo = computed(() => buildLeaderApprovalInfo(request.value))
  const leaderApprovalEvents = computed(() => buildLeaderApprovalEvents(request.value))
  const hasLeaderApprovalEvents = computed(() => leaderApprovalEvents.value.length > 0)
  const hasSingleLeaderApprovalEvent = computed(() => leaderApprovalEvents.value.length === 1)
  const leaderApprovalReadonlyMeta = computed(() => {
    const pieces = hasLeaderApprovalEvents.value ? [`${leaderApprovalEvents.value.length} 条批复记录`] : []
    if (leaderApprovalInfo.value.generatedDraftClaimNo) {
      pieces.push(`已生成报销草稿 ${leaderApprovalInfo.value.generatedDraftClaimNo}`)
    }
    return pieces.join(' · ')
  })
  const showApplicationLeaderOpinion = computed(() => (
    isApplicationDocument.value
    && hasLeaderApprovalEvents.value
  ))
  const heroFactItems = computed(() => [
    {
      key: 'document',
      label: isApplicationDocument.value ? '申请单号' : '报销单号',
      value: request.value.documentNo || request.value.id,
      icon: 'mdi mdi-camera-outline',
      valueClass: ''
    },
    {
      key: 'date',
      label: '单据申请日期',
      value: request.value.applyTime || request.value.occurredDisplay,
      icon: 'mdi mdi-calendar-month-outline',
      valueClass: ''
    },
    {
      key: 'amount',
      label: isApplicationDocument.value ? '预计金额' : '报销金额',
      value: request.value.amountDisplay,
      icon: '',
      valueClass: 'amount'
    },
    {
      key: 'type',
      label: isApplicationDocument.value ? '申请类型' : isTravelRequest.value ? '差旅类型' : '报销类型',
      value: request.value.typeLabel,
      icon: '',
      valueClass: ''
    }
  ])
  const progressSteps = computed(() => {
    const sourceSteps = Array.isArray(request.value.progressSteps) && request.value.progressSteps.length
      ? request.value.progressSteps
      : buildFallbackProgressSteps(request.value)
    return resolveProgressStepsForViewer(sourceSteps, {
      isApplicationDocument: isApplicationDocument.value,
      isCurrentDirectManagerApprover: isCurrentDirectManagerApprover.value
    })
  })
  const expenseCaseTimelineRefreshKey = computed(() => [
    request.value.claimId,
    request.value.status,
    request.value.node,
    request.value.updatedAt || request.value.updated_at
  ].map((value) => String(value || '').trim()).join('|'))
  const currentProgressRingMotion = {
    initial: { scale: 1, opacity: 0.34 },
    enter: {
      scale: [1, 1.42, 1.78],
      opacity: [0.34, 0.16, 0],
      transition: {
        duration: 3.2,
        repeat: Infinity,
        repeatType: 'loop',
        repeatDelay: 0.85,
        ease: 'easeOut',
        times: [0, 0.5, 1]
      }
    }
  }
  const submitConfirmAmountDisplay = computed(() =>
    isApplicationDocument.value ? (request.value.amountDisplay || expenseEditor.expenseTotal.value) : expenseEditor.expenseTotal.value
  )
  const applicationDetailFactItems = computed(() => buildApplicationDetailFactItems(request.value))
  const relatedApplicationFactItems = computed(() => buildRelatedApplicationFactItems(request.value))
  const applicationEditEditableFields = ['reason', 'time', 'location', 'transportMode']
  const applicationDetailEditableFactKeys = new Set([
    'reason',
    'location',
    'transport_mode',
    'trip_start_time',
    'trip_return_time',
    'time'
  ])

  watch(
    request,
    (nextRequest, previousRequest) => {
      expenseItems.value =
        Array.isArray(nextRequest.expenseItems)
          ? rebuildExpenseItems(nextRequest.expenseItems, nextRequest)
          : buildFallbackExpenseItems(nextRequest)
      if (nextRequest.claimId !== previousRequest?.claimId) {
        Object.keys(expenseAttachmentMeta).forEach((key) => {
          delete expenseAttachmentMeta[key]
        })
        riskSubmit.resetSubmitWorkState()
        attachmentPreview.closeAttachmentPreview()
      }
      expenseEditor.resetExpenseWorkState()
      cancelApplicationDetailEditor()
      void attachmentPreview.syncExpenseAttachmentMeta()
    },
    { immediate: true }
  )
  watch(
    () => request.value.claimId,
    () => {
      riskSubmit.clearRiskFlagPreviewSnapshot()
      expenseEditor.resetSmartEntryRecognitionApplications()
      expenseEditor.bindSmartEntryRecognitionTask()
    },
    { immediate: true }
  )

  function buildApplicationEditPreview() {
    const factEntries = applicationDetailFactItems.value
      .map((item) => [String(item?.label || '').trim(), String(item?.value || '').trim()])
      .filter(([label, value]) => label && value)
    const facts = new Map(factEntries)
    const pickFact = (...labels) => {
      for (const label of labels) {
        const value = facts.get(label)
        if (value) {
          return value
        }
      }
      return ''
    }
    const tripStart = pickFact('出发时间')
    const tripReturn = pickFact('返回时间')
    const time = tripStart && tripReturn && tripStart !== tripReturn
      ? `${tripStart} 至 ${tripReturn}`
      : pickFact('行程时间', '申请时间', '招待时间', '发生时间') || tripStart

    return {
      sourceText: '修改申请',
      modelReviewStatus: 'template',
      applicationEditMode: true,
      editableFields: applicationEditEditableFields,
      fields: {
        applicationType: pickFact('申请类型') || request.value.typeLabel || '费用申请',
        applicant: request.value.profileName || request.value.person || request.value.applicant || '',
        grade: pickFact('职级') || request.value.profileGrade || '',
        department: request.value.profileDepartment || request.value.departmentName || request.value.department || '',
        position: request.value.profilePosition || request.value.employeePosition || request.value.position || '',
        managerName: request.value.profileManager || request.value.managerName || request.value.manager || '',
        time,
        location: pickFact('地点') || request.value.location || request.value.city || '',
        reason: pickFact('事由') || request.value.reason || '',
        days: pickFact('天数'),
        transportMode: pickFact('出行方式'),
        lodgingDailyCap: pickFact('住宿上限/天'),
        subsidyDailyCap: pickFact('补贴标准/天'),
        transportPolicy: pickFact('交通费用口径'),
        policyEstimate: pickFact('规则测算参考'),
        amount: pickFact('系统预估费用', '用户预估费用', '预计金额') || request.value.amountDisplay || request.value.amount || ''
      }
    }
  }

  function buildApplicationEditDraftPayload() {
    const claimId = String(request.value?.claimId || '').trim()
    const claimNo = String(request.value?.claimNo || request.value?.documentNo || request.value?.id || '').trim()
    return {
      draft_type: 'expense_application',
      document_type: 'expense_application',
      claim_id: claimId,
      claim_no: claimNo,
      status: String(request.value?.status || request.value?.approvalKey || '').trim(),
      approval_stage: String(request.value?.node || request.value?.approvalStage || '待提交').trim(),
      title: String(request.value?.typeLabel || '费用申请').trim(),
      application_edit_mode: true
    }
  }

  function normalizeApplicationDetailEditorValue(value = '') {
    const text = String(value || '').trim()
    return text === '待补充' ? '' : text
  }

  function resolveApplicationDetailFactValue(key = '') {
    const targetKey = String(key || '').trim()
    return String(applicationDetailFactItems.value.find((item) => item?.key === targetKey)?.value || '').trim()
  }

  function buildApplicationDetailDateRange(startDate = '', endDate = '') {
    const start = String(startDate || '').trim()
    const end = String(endDate || '').trim()
    if (!start && !end) return ''
    if (!start) return end
    if (!end || end === start) return start
    return `${start} 至 ${end}`
  }

  function resolveApplicationDetailDays(startDate = '', endDate = '') {
    const start = String(startDate || '').trim()
    const end = String(endDate || '').trim()
    if (!start || !end) return ''
    const startTime = new Date(`${start}T00:00:00`).getTime()
    const endTime = new Date(`${end}T00:00:00`).getTime()
    if (!Number.isFinite(startTime) || !Number.isFinite(endTime) || endTime < startTime) {
      return ''
    }
    return `${Math.round((endTime - startTime) / 86400000) + 1}天`
  }

  function canEditApplicationDetailItem(item = {}) {
    return (
      canModifyApplication.value
      && applicationDetailEditableFactKeys.has(String(item?.key || '').trim())
    )
  }

  function isApplicationDetailEditing(item = {}) {
    return String(applicationDetailEditor.fieldKey || '') === String(item?.key || '')
  }

  function resolveApplicationDetailEditorControl(item = {}) {
    const key = String(item?.key || '').trim()
    if (['trip_start_time', 'trip_return_time', 'time'].includes(key)) {
      return 'date'
    }
    if (key === 'transport_mode') {
      return 'select'
    }
    return 'text'
  }

  function openApplicationDetailEditor(item = {}) {
    if (!canEditApplicationDetailItem(item) || applicationDetailEditor.saving) {
      return
    }
    applicationDetailEditor.fieldKey = String(item.key || '').trim()
    applicationDetailEditor.draftValue = normalizeApplicationDetailEditorValue(item.value)
  }

  function cancelApplicationDetailEditor() {
    applicationDetailEditor.fieldKey = ''
    applicationDetailEditor.draftValue = ''
  }

  function buildEditedApplicationPreview(item = {}) {
    const key = String(item?.key || '').trim()
    const nextValue = normalizeApplicationDetailEditorValue(applicationDetailEditor.draftValue)
    const preview = buildApplicationEditPreview()
    const fields = { ...(preview.fields || {}) }

    if (key === 'reason') {
      fields.reason = nextValue
    } else if (key === 'location') {
      fields.location = nextValue
    } else if (key === 'transport_mode') {
      fields.transportMode = nextValue
    } else if (key === 'time') {
      fields.time = nextValue
    } else if (key === 'trip_start_time' || key === 'trip_return_time') {
      const startDate = key === 'trip_start_time'
        ? nextValue
        : resolveApplicationDetailFactValue('trip_start_time')
      const endDate = key === 'trip_return_time'
        ? nextValue
        : resolveApplicationDetailFactValue('trip_return_time')
      fields.time = buildApplicationDetailDateRange(startDate, endDate)
      fields.days = resolveApplicationDetailDays(startDate, endDate) || fields.days
    }

    return normalizeApplicationPreview({
      ...preview,
      fields
    })
  }

  async function refreshEditedApplicationPreviewEstimate(preview = {}) {
    const estimateRequest = buildApplicationPolicyEstimateRequest(preview, currentUser.value || {})
    if (!estimateRequest.canCalculate) {
      return preview
    }
    try {
      const result = await calculateTravelReimbursement(estimateRequest.payload)
      return applyApplicationPolicyEstimateResult(preview, result, currentUser.value || {})
    } catch (error) {
      return applyApplicationPolicyEstimateError(preview, error, currentUser.value || {})
    }
  }

  async function saveApplicationDetailEdit(item = {}) {
    if (!isApplicationDetailEditing(item) || applicationDetailEditor.saving) {
      return
    }
    if (!String(request.value?.claimId || '').trim()) {
      toast('当前申请缺少单据标识，暂不能修改。')
      return
    }

    applicationDetailEditor.saving = true
    try {
      const preview = await refreshEditedApplicationPreviewEstimate(buildEditedApplicationPreview(item))
      const payload = await runAiApplicationPreviewAction({
        actionType: AI_APPLICATION_ACTION_SAVE_DRAFT,
        applicationPreview: preview,
        currentUser: currentUser.value || {},
        draftPayload: buildApplicationEditDraftPayload()
      })
      const draftPayload = payload?.result?.draft_payload || payload?.draft_payload || {}
      emit('request-updated', {
        claimId: String(draftPayload.claim_id || request.value.claimId || '').trim(),
        claimNo: String(draftPayload.claim_no || request.value.claimNo || request.value.documentNo || '').trim(),
        status: String(draftPayload.status || request.value.status || '').trim(),
        approvalStage: String(draftPayload.approval_stage || request.value.node || '').trim()
      })
      cancelApplicationDetailEditor()
      toast('申请信息已更新。')
    } catch (error) {
      toast(error?.message || '申请信息更新失败，请稍后重试。')
    } finally {
      applicationDetailEditor.saving = false
    }
  }

  onBeforeUnmount(() => {
    riskSubmit.disposeRiskSubmit()
    expenseEditor.disposeExpenseEditor()
    attachmentPreview.closeAttachmentPreview()
  })

  return {
    emit,
    actionBusy,
    ...attachmentPreview,
    ...approvalFlow,
    ...expenseEditor,
    ...paymentFlow,
    ...riskSubmit,
    APPLICATION_TRANSPORT_MODE_OPTIONS,
    applicationDetailEditor,
    applicationDetailFactItems,
    relatedApplicationFactItems,
    canEditApplicationDetailItem,
    canDeleteRequest,
    canManageCurrentClaim,
    canModifyApplication,
    canOpenAiEntry,
    canApproveRequest,
    canReturnRequest,
    currentProgressRingMotion,
    expenseCaseTimelineRefreshKey,
    expenseItems,
    expenseTypeOptions: EXPENSE_TYPE_OPTIONS,
    hasLeaderApprovalEvents,
    hasSingleLeaderApprovalEvent,
    heroFactItems,
    isApplicationDocument,
    isApplicationDetailEditing,
    isDraftRequest,
    isEditableRequest,
    isTravelRequest,
    leaderApprovalEvents,
    leaderApprovalReadonlyMeta,
    profile,
    progressSteps,
    request,
    cancelApplicationDetailEditor,
    openApplicationDetailEditor,
    resolveExpenseReasonHelper,
    resolveExpenseReasonPlaceholder,
    resolveApplicationDetailEditorControl,
    saveApplicationDetailEdit,
    showApplicationLeaderOpinion,
    showBudgetAnalysis,
    showStageRiskAdvice,
    submitConfirmAmountDisplay
  }
}
