import {
  buildLinkedApplicationReferenceIndex,
  buildRequiredApplicationActions,
  isClaimOwnedByCurrentUser,
  isExpenseApplicationClaim,
  isUsableRequiredApplicationClaim,
  normalizeRequiredApplicationCandidate,
  resolveRequiredApplicationReimbursementType
} from './travelReimbursementApplicationLinkModel.js'
import {
  buildReimbursementDraftActions,
  buildSkipRequiredApplicationLinkAction
} from './travelReimbursementDraftBranchModel.js'

export {
  CANCEL_STANDALONE_REIMBURSEMENT_DRAFT_ACTION,
  CONTINUE_REIMBURSEMENT_DRAFT_ACTION,
  CREATE_STANDALONE_REIMBURSEMENT_DRAFT_ACTION,
  SKIP_REQUIRED_APPLICATION_LINK_ACTION,
  SKIP_REIMBURSEMENT_DRAFT_CHECK_ACTION,
  buildContinueReimbursementDraftAction,
  buildCreateStandaloneReimbursementDraftAction,
  buildReimbursementDraftActions,
  buildReimbursementDraftContinuationText,
  buildSkipReimbursementDraftCheckAction,
  buildSkipRequiredApplicationLinkAction,
  buildStandaloneReimbursementDraftConfirmationActions,
  buildStandaloneReimbursementDraftConfirmationText,
  buildViewReimbursementDraftAction
} from './travelReimbursementDraftBranchModel.js'

const REIMBURSEMENT_DRAFT_STATUSES = new Set(['draft', 'supplement', 'returned'])

const STATUS_LABELS = {
  draft: '草稿',
  supplement: '待补充',
  returned: '已退回',
  submitted: '审批中',
  approved: '已审批',
  completed: '已完成',
  archived: '已归档',
  pending_payment: '待付款',
  paid: '已付款'
}

const EXPENSE_TYPE_LABELS = {
  travel: '差旅费',
  meal: '业务招待费'
}
const REIMBURSEMENT_ASSOCIATION_STEP_DELAY_MS = 320
export const REIMBURSEMENT_ASSOCIATION_QUERY_TIMEOUT_MS = 12000
const REIMBURSEMENT_ASSOCIATION_QUERY_PARAMS = Object.freeze({ page: 1, pageSize: 100 })
const REIMBURSEMENT_ASSOCIATION_QUERY_TIMEOUT_MESSAGE = '查询可关联申请单超时，请稍后重试。'

function normalizeText(value) {
  return String(value || '').trim()
}

function normalizeLower(value) {
  return normalizeText(value).toLowerCase()
}

function normalizeExpenseType(claim) {
  return normalizeLower(claim?.expense_type || claim?.expenseType || claim?.type_code || claim?.typeCode)
}

function normalizeClaimStatus(claim) {
  return normalizeLower(claim?.status || claim?.state || claim?.approval_status || claim?.approvalStatus)
}

function extractClaims(claimsPayload) {
  return Array.isArray(claimsPayload)
    ? claimsPayload
    : Array.isArray(claimsPayload?.items)
      ? claimsPayload.items
      : Array.isArray(claimsPayload?.claims)
        ? claimsPayload.claims
        : []
}

function escapeHtml(value = '') {
  return String(value || '')
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
}

function toTimestamp(value) {
  const date = new Date(value)
  return Number.isNaN(date.getTime()) ? 0 : date.getTime()
}

function formatAmount(value) {
  const numberValue = Number(String(value ?? '').replace(/[^\d.-]/g, ''))
  if (!Number.isFinite(numberValue) || numberValue <= 0) {
    return ''
  }
  return `¥${new Intl.NumberFormat('zh-CN', {
    minimumFractionDigits: Number.isInteger(numberValue) ? 0 : 2,
    maximumFractionDigits: 2
  }).format(numberValue)}`
}

function formatCompactDateTime(value) {
  const text = normalizeText(value)
  if (!text) {
    return ''
  }
  const matched = text.match(/^(\d{4}-\d{2}-\d{2})[T\s](\d{2}:\d{2})/)
  if (matched) {
    return `${matched[1]} ${matched[2]}`
  }
  return text
}

function formatDraftAmount(value, label = '') {
  const explicitLabel = normalizeText(label)
  if (explicitLabel) {
    return explicitLabel
  }
  return formatAmount(value) || '待确认'
}

function resolveCurrentUser(currentUser) {
  return currentUser?.value && typeof currentUser.value === 'object'
    ? currentUser.value
    : currentUser && typeof currentUser === 'object'
      ? currentUser
      : {}
}

function waitForReimbursementAssociationStep() {
  return new Promise((resolve) => {
    globalThis.setTimeout(resolve, REIMBURSEMENT_ASSOCIATION_STEP_DELAY_MS)
  })
}

function createReimbursementAssociationQueryTimeoutError() {
  const error = new Error(REIMBURSEMENT_ASSOCIATION_QUERY_TIMEOUT_MESSAGE)
  error.code = 'REQUEST_TIMEOUT'
  return error
}

export function isReimbursementAssociationQueryTimeoutError(error) {
  return error?.code === 'REQUEST_TIMEOUT'
}

export function buildReimbursementAssociationQueryFailedText(error) {
  if (isReimbursementAssociationQueryTimeoutError(error)) {
    return '查询可关联申请单超时。您可以稍后重试，也可以选择不关联申请单，单独新建报销单。'
  }
  return '查询可关联申请单时出现异常。您可以稍后重试，也可以选择不关联申请单，单独新建报销单。'
}

export async function fetchReimbursementAssociationClaims({
  fetchExpenseClaims,
  timeoutMs = REIMBURSEMENT_ASSOCIATION_QUERY_TIMEOUT_MS
} = {}) {
  if (typeof fetchExpenseClaims !== 'function') {
    return { items: [] }
  }

  const queryPromise = Promise.resolve(fetchExpenseClaims(
    REIMBURSEMENT_ASSOCIATION_QUERY_PARAMS,
    {
      timeoutMs,
      timeoutMessage: REIMBURSEMENT_ASSOCIATION_QUERY_TIMEOUT_MESSAGE
    }
  ))

  const normalizedTimeoutMs = Number(timeoutMs)
  if (!Number.isFinite(normalizedTimeoutMs) || normalizedTimeoutMs <= 0) {
    return queryPromise
  }

  let timeoutId = 0
  const timeoutPromise = new Promise((resolve, reject) => {
    timeoutId = globalThis.setTimeout(() => {
      reject(createReimbursementAssociationQueryTimeoutError())
    }, normalizedTimeoutMs)
  })

  try {
    return await Promise.race([queryPromise, timeoutPromise])
  } finally {
    if (timeoutId) {
      globalThis.clearTimeout(timeoutId)
    }
  }
}

export function filterReimbursementAssociationCandidates(claimsPayload, currentUser = {}) {
  const claims = extractClaims(claimsPayload)
  const linkedApplicationReferences = buildLinkedApplicationReferenceIndex(claims)

  return claims
    .filter((claim) => (
      isExpenseApplicationClaim(claim)
      && isUsableRequiredApplicationClaim(claim, linkedApplicationReferences)
      && isClaimOwnedByCurrentUser(claim, currentUser)
    ))
    .map(normalizeRequiredApplicationCandidate)
    .sort((left, right) => toTimestamp(right.application_date) - toTimestamp(left.application_date))
}

export function normalizeReimbursementDraftCandidate(claim = {}) {
  const status = normalizeClaimStatus(claim)
  const amount = normalizeText(claim?.amount || claim?.total_amount || claim?.totalAmount)
  const createdAt = normalizeText(
    claim?.updated_at
      || claim?.updatedAt
      || claim?.created_at
      || claim?.createdAt
      || claim?.submitted_at
      || claim?.submittedAt
  )

  return {
    id: normalizeText(claim?.id || claim?.claim_id || claim?.claimId),
    claim_no: normalizeText(claim?.claim_no || claim?.claimNo),
    expense_type: normalizeExpenseType(claim),
    reason: normalizeText(claim?.reason || claim?.business_reason || claim?.description || claim?.title),
    location: normalizeText(claim?.location || claim?.business_location || claim?.businessLocation),
    amount,
    amount_label: formatAmount(amount),
    status,
    status_label: STATUS_LABELS[status] || normalizeText(claim?.status_label || claim?.statusLabel || claim?.approval_stage || claim?.approvalStage || status),
    created_at: formatCompactDateTime(createdAt),
    application_date: createdAt
  }
}

export function filterReimbursementDraftCandidates(claimsPayload, currentUser = {}) {
  return extractClaims(claimsPayload)
    .filter((claim) => (
      !isExpenseApplicationClaim(claim)
      && REIMBURSEMENT_DRAFT_STATUSES.has(normalizeClaimStatus(claim))
      && isClaimOwnedByCurrentUser(claim, currentUser)
    ))
    .map(normalizeReimbursementDraftCandidate)
    .sort((left, right) => toTimestamp(right.application_date) - toTimestamp(left.application_date))
}

export function buildReimbursementAssociationSelectionText(applications) {
  const candidates = Array.isArray(applications) ? applications : []
  return [
    '### 可关联申请单',
    '',
    '我先检查了您名下的报销草稿，没有查到可继续的报销草稿。',
    '',
    '接下来先查询可关联申请单，为您筛选出名下已审批且尚未关联报销的记录。',
    '',
    `查到 ${candidates.length} 个已审批且尚未关联报销的申请单。您可以从中选择一个进行关联，也可以不关联、直接单独新建报销单。`,
    '',
    buildReimbursementAssociationCardsHtml(candidates),
    '',
    '请通过下方按钮确认是否关联申请单。'
  ].join('\n')
}

export function buildReimbursementAssociationMissingText() {
  return [
    '我先检查了您名下的报销草稿，没有查到可继续的报销草稿。',
    '',
    '接下来先查询可关联申请单，为您筛选出名下已审批且尚未关联报销的记录。',
    '',
    '暂时没有查到已审批且尚未关联报销的申请单。您仍然可以选择单独新建报销单，后续按报销类型继续补充信息。'
  ].join('\n')
}

export function buildReimbursementDraftSelectionText(drafts) {
  const candidates = Array.isArray(drafts) ? drafts : []
  return [
    '### 可继续报销草稿',
    '',
    '我先检查了您名下的报销草稿。',
    '',
    `查到 ${candidates.length} 个可继续的报销草稿。您可以查看草稿详情，或继续把附件、说明关联到该草稿；如果这是一次新的报销，请独立新建报销单。`,
    '',
    buildReimbursementDraftCardsHtml(candidates),
    '',
    '请通过下方三个按钮选择下一步。'
  ].join('\n')
}

export function buildReimbursementAssociationActions(applications, originalMessage = '') {
  const sourceText = normalizeText(originalMessage) || '我要报销'
  return [
    ...buildRequiredApplicationActions(applications, 'select_required_application').map((action) => ({
      ...action,
      payload: {
        ...(action.payload || {}),
        original_message: sourceText
      }
    })),
    buildSkipRequiredApplicationLinkAction(sourceText)
  ]
}

function buildAssociationCardFieldHtml(label = '', value = '', options = {}) {
  const text = normalizeText(value)
  if (!text) {
    return ''
  }
  const fieldClass = options.fieldClass ? ` ${options.fieldClass}` : ''
  const valueClass = options.valueClass ? ` ${options.valueClass}` : ''
  return [
    `<div class="ai-document-card__field${fieldClass}">`,
    `<span class="ai-document-card__label">${escapeHtml(label)}</span>`,
    `<strong class="ai-document-card__value${valueClass}">${escapeHtml(text)}</strong>`,
    '</div>'
  ].join('')
}

function buildReimbursementDraftCardHtml(draft = {}) {
  const statusLabel = normalizeText(draft.status_label) || '草稿'
  const title = normalizeText(EXPENSE_TYPE_LABELS[normalizeLower(draft.expense_type)] || draft.expense_type) || '报销草稿'
  const summaryHtml = [
    buildAssociationCardFieldHtml('金额', formatDraftAmount(draft.amount, draft.amount_label), {
      valueClass: 'ai-document-card__amount'
    }),
    buildAssociationCardFieldHtml('更新时间', draft.created_at || '待确认')
  ].join('')
  const detailsHtml = [
    buildAssociationCardFieldHtml('地点', draft.location || '待补充'),
    buildAssociationCardFieldHtml('单据编号', draft.claim_no || '未编号草稿', {
      valueClass: 'ai-document-card__number'
    }),
    buildAssociationCardFieldHtml('事由', draft.reason || '待补充'),
    buildAssociationCardFieldHtml('单据类型', `报销单 · ${title}`),
    buildAssociationCardFieldHtml('操作', '使用下方按钮查看、关联或新建', {
      fieldClass: 'ai-document-card__field--action'
    })
  ].join('')

  return [
    '<article class="ai-document-card ai-document-card--expense is-pending" aria-label="报销草稿详情">',
    '<header class="ai-document-card__head">',
    `<strong class="ai-document-card__reason">${escapeHtml(title)}</strong>`,
    `<span class="ai-document-card__status">${escapeHtml(statusLabel)}</span>`,
    '</header>',
    '<div class="ai-document-card__body">',
    summaryHtml ? `<div class="ai-document-card__summary">${summaryHtml}</div>` : '',
    '<div class="ai-document-card__details">',
    detailsHtml,
    '</div>',
    '</div>',
    '</article>'
  ].join('')
}

export function buildReimbursementDraftCardsHtml(drafts = []) {
  const candidates = (Array.isArray(drafts) ? drafts : []).slice(0, 5)
  if (!candidates.length) {
    return ''
  }
  return [
    '<!-- ai-trusted-html:start -->',
    '<section class="ai-document-card-list" aria-label="可继续报销草稿">',
    ...candidates.map((draft) => buildReimbursementDraftCardHtml(draft)),
    '</section>',
    '<!-- ai-trusted-html:end -->'
  ].join('\n')
}

function buildReimbursementAssociationCardHtml(application = {}) {
  const statusLabel = normalizeText(application.status_label) || '已审批'
  const statusTone = ['已审批', '已完成', '已归档'].some((item) => statusLabel.includes(item))
    ? 'is-success'
    : 'is-pending'
  const title = normalizeText(resolveRequiredApplicationReimbursementType(application).expenseTypeLabel) || '费用申请'
  const summaryHtml = [
    buildAssociationCardFieldHtml('时间', application.business_time || '待补充'),
    buildAssociationCardFieldHtml('预计金额', application.amount_label || application.amount || '待确认', {
      valueClass: 'ai-document-card__amount'
    })
  ].join('')
  const detailsHtml = [
    buildAssociationCardFieldHtml('地点', application.location || '待补充'),
    buildAssociationCardFieldHtml('单据编号', application.claim_no || '未编号申请单', {
      valueClass: 'ai-document-card__number'
    }),
    buildAssociationCardFieldHtml('事由', application.reason || '待补充'),
    buildAssociationCardFieldHtml('单据类型', `申请单 · ${title}`),
    buildAssociationCardFieldHtml('操作', '使用下方按钮关联', {
      fieldClass: 'ai-document-card__field--action'
    })
  ].join('')

  return [
    `<article class="ai-document-card ai-document-card--application ${statusTone}" aria-label="申请单详情">`,
    '<header class="ai-document-card__head">',
    `<strong class="ai-document-card__reason">${escapeHtml(title)}</strong>`,
    `<span class="ai-document-card__status">${escapeHtml(statusLabel)}</span>`,
    '</header>',
    '<div class="ai-document-card__body">',
    summaryHtml ? `<div class="ai-document-card__summary">${summaryHtml}</div>` : '',
    '<div class="ai-document-card__details">',
    detailsHtml,
    '</div>',
    '</div>',
    '</article>'
  ].join('')
}

export function buildReimbursementAssociationCardsHtml(applications = []) {
  const candidates = (Array.isArray(applications) ? applications : []).slice(0, 5)
  if (!candidates.length) {
    return ''
  }
  return [
    '<!-- ai-trusted-html:start -->',
    '<section class="ai-document-card-list" aria-label="可关联申请单">',
    ...candidates.map((application) => buildReimbursementAssociationCardHtml(application)),
    '</section>',
    '<!-- ai-trusted-html:end -->'
  ].join('\n')
}

function resolveAssociationStatusGroup(application = {}) {
  const status = normalizeLower(application.status)
  if (['approved', 'completed', 'archived'].includes(status)) {
    return { key: 'completed', label: '已审批' }
  }
  if (['submitted', 'review', 'pending'].includes(status)) {
    return { key: 'in_progress', label: '审批中' }
  }
  if (['returned', 'rejected'].includes(status)) {
    return { key: 'draft', label: '待完善' }
  }
  return { key: 'other', label: '其他状态' }
}

export function buildReimbursementAssociationQueryPayload(applications = []) {
  const candidates = Array.isArray(applications) ? applications : []
  const records = candidates.map((application) => {
    const statusGroup = resolveAssociationStatusGroup(application)
    return {
      claimId: normalizeText(application.id || application.claim_no),
      claimNo: normalizeText(application.claim_no) || '未编号申请单',
      employeeName: '',
      expenseType: normalizeText(application.expense_type),
      expenseTypeLabel: resolveRequiredApplicationReimbursementType(application).expenseTypeLabel,
      amount: Number(String(application.amount || '').replace(/,/g, '')) || 0,
      amountDisplay: normalizeText(application.amount_label || application.amount) || '待确认',
      status: normalizeText(application.status),
      statusLabel: normalizeText(application.status_label) || statusGroup.label,
      statusGroup: statusGroup.key,
      statusGroupLabel: statusGroup.label,
      approvalStage: '',
      documentDate: normalizeText(application.application_date),
      occurredAt: '',
      reason: normalizeText(application.reason) || '费用申请',
      location: normalizeText(application.location),
      riskItems: [],
      summary: normalizeText(application.reason) || '费用申请',
      dateDisplay: normalizeText(application.business_time || application.application_date) || '待补充日期'
    }
  })
  const statusGroups = records.reduce((groups, record) => {
    const key = record.statusGroup || 'other'
    const existing = groups.get(key) || {
      key,
      label: record.statusGroupLabel || '其他状态',
      count: 0
    }
    existing.count += 1
    groups.set(key, existing)
    return groups
  }, new Map())
  return {
    resultType: 'expense_claim_list',
    scopeLabel: '可关联申请单',
    selectionMode: 'reimbursement_application_association',
    selectionLocked: false,
    selectedClaimId: '',
    title: '可关联申请单',
    emptyText: '当前没有可关联的已审批申请单。',
    recentWindowApplied: false,
    windowDays: null,
    windowStartDate: '',
    windowEndDate: '',
    recordCount: records.length,
    previewCount: records.length,
    previewLimit: 5,
    olderRecordCount: 0,
    hasMoreInWindow: false,
    totalAmount: records.reduce((sum, record) => sum + Number(record.amount || 0), 0),
    statusGroups: Array.from(statusGroups.values()),
    records,
    currentPage: 1
  }
}

export function buildReimbursementAssociationThinkingEvents(stage = 'intent', options = {}) {
  const candidateCount = Number(options.candidateCount || 0)
  const failed = stage === 'failed'
  const stageOrder = {
    intent: 0,
    query: 1,
    filter: 2,
    completed: 4,
    failed: 4
  }
  const currentOrder = stageOrder[stage] ?? 0
  const resolveStatus = (index) => {
    if (failed && index >= currentOrder - 1) {
      return 'failed'
    }
    if (currentOrder > index) {
      return 'completed'
    }
    if (currentOrder === index) {
      return 'running'
    }
    return 'pending'
  }

  return [
    {
      eventId: 'reimbursement-association-intent',
      title: '判断用户意图',
      content: currentOrder > 0
        ? '已识别为报销创建请求，需要先检查是否已有报销草稿。'
        : '正在判断用户是否要创建报销单，并确认是否需要先进入草稿与申请单关联检查。',
      status: resolveStatus(0)
    },
    {
      eventId: 'reimbursement-draft-check',
      title: '检查报销草稿',
      content: currentOrder > 1
        ? '已完成报销草稿检查，继续判断是否需要进入申请单关联。'
        : '正在查询您名下是否存在可继续的报销草稿。',
      status: resolveStatus(1)
    },
    {
      eventId: 'reimbursement-association-query',
      title: '查询可关联申请单',
      content: currentOrder > 2
        ? `已完成申请单查询与筛选，命中 ${candidateCount} 张可推荐单据。`
        : '如未发现可继续草稿，就查询您名下已审批且尚未关联报销的申请单。',
      status: resolveStatus(2)
    },
    {
      eventId: 'reimbursement-association-card',
      title: '生成单据卡片',
      content: currentOrder > 3
        ? '已按草稿优先、申请单后置的顺序生成单据卡片和快捷按钮。'
        : '等待查询结果返回后，以卡片形式展示可继续或可关联的单据。',
      status: resolveStatus(3)
    }
  ]
}

export function buildReimbursementAssociationSubmitOptions(application = {}, originalMessage = '') {
  const sourceText = normalizeText(originalMessage) || '我要报销'
  const resolvedType = resolveRequiredApplicationReimbursementType(application)
  const applicationId = normalizeText(application.application_claim_id || application.id)
  const applicationNo = normalizeText(application.application_claim_no || application.claim_no)
  const applicationReason = normalizeText(application.application_reason || application.reason)
  const applicationLocation = normalizeText(application.application_location || application.location)
  const applicationBusinessTime = normalizeText(application.application_business_time || application.business_time)
  const applicationTransportMode = normalizeText(application.application_transport_mode || application.transport_mode)

  const rawText = [
    sourceText,
    `用户选择报销场景：${resolvedType.expenseTypeLabel}`,
    applicationNo && `关联申请单：${applicationNo}`
  ].filter(Boolean).join('\n')

  return {
    rawText,
    userText: `关联申请单 ${applicationNo || ''}`.trim() || '关联申请单',
    pendingText: `已关联申请单，正在生成${resolvedType.expenseTypeLabel}草稿...`,
    systemGenerated: true,
    skipUserMessage: true,
    skipDraftAssociationPrompt: true,
    associationConfirmed: true,
    extraContext: {
      draft_claim_id: '',
      review_action: 'save_draft',
      user_input_text: sourceText,
      expense_scene_selection: {
        expense_type: resolvedType.expenseType,
        expense_type_label: resolvedType.expenseTypeLabel,
        original_message: sourceText,
        application_claim_id: applicationId,
        application_claim_no: applicationNo
      },
      review_form_values: {
        expense_type: resolvedType.expenseTypeLabel,
        reason: applicationReason,
        location: applicationLocation,
        time_range: applicationBusinessTime,
        transport_mode: applicationTransportMode,
        amount: '',
        application_claim_id: applicationId,
        application_claim_no: applicationNo,
        application_reason: applicationReason,
        application_location: applicationLocation,
        application_amount: application.application_amount || application.amount || '',
        application_amount_label: application.application_amount_label || application.amount_label || '',
        application_business_time: applicationBusinessTime,
        application_days: application.application_days || application.days || '',
        application_transport_mode: applicationTransportMode,
        application_lodging_daily_cap: application.application_lodging_daily_cap || application.lodging_daily_cap || '',
        application_subsidy_daily_cap: application.application_subsidy_daily_cap || application.subsidy_daily_cap || '',
        application_transport_policy: application.application_transport_policy || application.transport_policy || '',
        application_policy_estimate: application.application_policy_estimate || application.policy_estimate || '',
        application_rule_name: application.application_rule_name || application.rule_name || '',
        application_rule_version: application.application_rule_version || application.rule_version || ''
      }
    }
  }
}

export async function buildReimbursementAssociationPromptPayload({
  originalMessage = '我要报销',
  fetchExpenseClaims,
  currentUser,
  skipDraftCheck = false,
  queryTimeoutMs = REIMBURSEMENT_ASSOCIATION_QUERY_TIMEOUT_MS
} = {}) {
  const sourceText = normalizeText(originalMessage) || '我要报销'
  try {
    const claimsPayload = await fetchReimbursementAssociationClaims({
      fetchExpenseClaims,
      timeoutMs: queryTimeoutMs
    })
    const user = resolveCurrentUser(currentUser)
    const drafts = skipDraftCheck ? [] : filterReimbursementDraftCandidates(claimsPayload, user)
    if (drafts.length) {
      return {
        text: buildReimbursementDraftSelectionText(drafts),
        meta: ['等待选择报销草稿'],
        suggestedActions: buildReimbursementDraftActions(drafts, sourceText),
        queryPayload: null,
        candidateCount: drafts.length
      }
    }
    const candidates = filterReimbursementAssociationCandidates(claimsPayload, user)
    return {
      text: candidates.length
        ? buildReimbursementAssociationSelectionText(candidates)
        : buildReimbursementAssociationMissingText(),
      meta: candidates.length ? ['等待关联申请单'] : ['无可关联申请单'],
      suggestedActions: buildReimbursementAssociationActions(candidates, sourceText),
      queryPayload: buildReimbursementAssociationQueryPayload(candidates),
      candidateCount: candidates.length
    }
  } catch (error) {
    console.warn('Fetch reimbursement association applications failed:', error)
    return {
      text: buildReimbursementAssociationQueryFailedText(error),
      meta: ['申请单查询失败'],
      suggestedActions: buildReimbursementAssociationActions([], sourceText),
      queryPayload: buildReimbursementAssociationQueryPayload([]),
      candidateCount: 0,
      failed: true
    }
  }
}

export async function pushReimbursementAssociationPromptMessage({
  rawText,
  createMessage,
  messages,
  nextTick,
  scrollToBottom,
  persistSessionState,
  fetchExpenseClaims,
  currentUser,
  skipDraftCheck = false,
  queryTimeoutMs = REIMBURSEMENT_ASSOCIATION_QUERY_TIMEOUT_MS
} = {}) {
  const pendingMessage = createMessage('assistant', '', [], {
    pending: true,
    meta: ['思考中'],
    stewardPlan: {
      streamStatus: 'streaming',
      thinkingEvents: buildReimbursementAssociationThinkingEvents('intent')
    },
    suggestedActions: []
  })
  messages.value.push(pendingMessage)
  nextTick(scrollToBottom)
  persistSessionState()
  await waitForReimbursementAssociationStep()

  pendingMessage.stewardPlan = {
    ...(pendingMessage.stewardPlan || {}),
    streamStatus: 'streaming',
    thinkingEvents: buildReimbursementAssociationThinkingEvents('query')
  }
  nextTick(scrollToBottom)

  const associationPrompt = await buildReimbursementAssociationPromptPayload({
    originalMessage: rawText,
    fetchExpenseClaims,
    currentUser,
    skipDraftCheck,
    queryTimeoutMs
  })
  pendingMessage.stewardPlan = {
    ...(pendingMessage.stewardPlan || {}),
    streamStatus: associationPrompt.failed ? 'failed' : 'streaming',
    thinkingEvents: buildReimbursementAssociationThinkingEvents(
      associationPrompt.failed ? 'failed' : 'filter',
      { candidateCount: associationPrompt.candidateCount }
    )
  }
  nextTick(scrollToBottom)
  if (!associationPrompt.failed) {
    await waitForReimbursementAssociationStep()
  }

  pendingMessage.text = associationPrompt.text
  pendingMessage.pending = false
  pendingMessage.meta = associationPrompt.meta
  pendingMessage.suggestedActions = associationPrompt.suggestedActions
  pendingMessage.queryPayload = associationPrompt.queryPayload
  pendingMessage.stewardPlan = {
    ...(pendingMessage.stewardPlan || {}),
    streamStatus: associationPrompt.failed ? 'failed' : 'completed',
    thinkingEvents: buildReimbursementAssociationThinkingEvents(
      associationPrompt.failed ? 'failed' : 'completed',
      { candidateCount: associationPrompt.candidateCount }
    )
  }
  nextTick(scrollToBottom)
  persistSessionState()
}
