import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { fileURLToPath } from 'node:url'

import {
  APPLICATION_WELCOME_QUICK_ACTIONS,
  APPROVAL_WELCOME_QUICK_ACTIONS,
  ASSISTANT_SESSION_MODE_OPTIONS,
  EXPENSE_WELCOME_QUICK_ACTIONS,
  SESSION_TYPE_APPLICATION,
  SESSION_TYPE_APPROVAL,
  SESSION_TYPE_EXPENSE,
  SESSION_TYPE_KNOWLEDGE,
  buildWelcomeQuickActions
} from '../src/views/scripts/travelReimbursementConversationModel.js'
import {
  GUIDED_ACTION_CONFIRM_REIMBURSEMENT_REVIEW,
  GUIDED_ACTION_CONTINUE_FILLING,
  GUIDED_ACTION_OPEN_TRAVEL_CALCULATOR,
  GUIDED_ACTION_START_APPLICATION,
  GUIDED_ACTION_PROCESS_INTERRUPTION,
  GUIDED_ACTION_SELECT_EXPENSE_TYPE,
  GUIDED_ACTION_SELECT_REQUIRED_APPLICATION,
  GUIDED_ACTION_SELECT_QUERY_MODE,
  GUIDED_ACTION_SELECT_QUERY_STATUS,
  GUIDED_ACTION_START_REIMBURSEMENT,
  GUIDED_ACTION_START_STATUS_QUERY,
  GUIDED_FLOW_MODE_REIMBURSEMENT,
  GUIDED_FLOW_MODE_STATUS_QUERY,
  applyGuidedReimbursementAnswer,
  buildGuidedExpenseTypeActions,
  buildGuidedInterruptionActions,
  buildGuidedQueryModeActions,
  buildGuidedQueryStatusActions,
  buildGuidedReimbursementSummaryText,
  buildGuidedReviewConfirmationActions,
  buildGuidedReviewSubmitOptions,
  buildGuidedStatusQueryText,
  buildGuidedStepPromptText,
  createEmptyGuidedFlowState,
  createGuidedReimbursementState,
  createGuidedStatusQueryState,
  isGuidedReimbursementReadyForReview,
  normalizeGuidedFlowState,
  selectGuidedRequiredApplication,
  selectGuidedExpenseType,
  selectGuidedQueryMode,
  shouldConfirmGuidedInterruption,
  waitForGuidedApplicationSelection
} from '../src/views/scripts/travelReimbursementGuidedFlowModel.js'
import {
  buildRequiredApplicationActions,
  buildRequiredApplicationMissingText,
  buildRequiredApplicationSelectionText,
  filterRequiredApplicationCandidates,
  requiresApplicationBeforeReimbursement
} from '../src/views/scripts/travelReimbursementApplicationLinkModel.js'
import {
  SKIP_REQUIRED_APPLICATION_LINK_ACTION,
  buildReimbursementAssociationActions,
  buildReimbursementAssociationMissingText,
  buildReimbursementAssociationQueryPayload,
  buildReimbursementAssociationSelectionText,
  buildReimbursementAssociationSubmitOptions,
  buildReimbursementAssociationThinkingEvents
} from '../src/views/scripts/travelReimbursementAssociationGateModel.js'
import {
  ASSISTANT_SCOPE_ACTION_SWITCH,
  resolveAssistantScopeGuard
} from '../src/utils/assistantSessionScope.js'

const createViewScript = readFileSync(
  fileURLToPath(new URL('../src/views/scripts/TravelReimbursementCreateView.js', import.meta.url)),
  'utf8'
)
const guidedFlowScript = readFileSync(
  fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementGuidedFlow.js', import.meta.url)),
  'utf8'
)
const suggestedActionsScript = readFileSync(
  fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSuggestedActions.js', import.meta.url)),
  'utf8'
)
const guidedModelScript = readFileSync(
  fileURLToPath(new URL('../src/views/scripts/travelReimbursementGuidedFlowModel.js', import.meta.url)),
  'utf8'
)
const sessionStateScript = readFileSync(
  fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSessionState.js', import.meta.url)),
  'utf8'
)
const submitComposerScript = readFileSync(
  fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSubmitComposer.js', import.meta.url)),
  'utf8'
)
const submitDraftPreflightScript = readFileSync(
  fileURLToPath(new URL('../src/views/scripts/travelReimbursementSubmitDraftPreflight.js', import.meta.url)),
  'utf8'
)
const messageHandlersScript = readFileSync(
  fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementCreateViewMessageHandlers.js', import.meta.url)),
  'utf8'
)

test('assistant session modes expose independent quick actions', () => {
  assert.deepEqual(
    ASSISTANT_SESSION_MODE_OPTIONS.map((item) => item.label),
    ['小财管家', '申请助手', '报销助手', '审核助手', '财务知识助手', '预算编制助手']
  )
  assert.deepEqual(
    EXPENSE_WELCOME_QUICK_ACTIONS.map((item) => item.label),
    ['快速发起报销', '查询单据状态', '差旅计算器']
  )
  assert.deepEqual(
    EXPENSE_WELCOME_QUICK_ACTIONS.map((item) => item.action),
    [
      GUIDED_ACTION_START_REIMBURSEMENT,
      GUIDED_ACTION_START_STATUS_QUERY,
      GUIDED_ACTION_OPEN_TRAVEL_CALCULATOR
    ]
  )
  assert.ok(EXPENSE_WELCOME_QUICK_ACTIONS.every((item) => !item.prompt))
  assert.equal(buildWelcomeQuickActions(SESSION_TYPE_EXPENSE).length, 3)
  assert.deepEqual(
    buildWelcomeQuickActions(SESSION_TYPE_APPLICATION).map((item) => item.label),
    APPLICATION_WELCOME_QUICK_ACTIONS.map((item) => item.label)
  )
  assert.equal(buildWelcomeQuickActions(SESSION_TYPE_APPLICATION)[0].action, GUIDED_ACTION_START_APPLICATION)
  assert.ok(!buildWelcomeQuickActions(SESSION_TYPE_APPLICATION)[0].prompt)
  assert.ok(
    buildWelcomeQuickActions(SESSION_TYPE_APPLICATION).every((item) => item.action !== GUIDED_ACTION_OPEN_TRAVEL_CALCULATOR)
  )
  assert.ok(
    buildWelcomeQuickActions(SESSION_TYPE_APPROVAL).every((item) => item.action !== GUIDED_ACTION_OPEN_TRAVEL_CALCULATOR)
  )
  assert.match(guidedFlowScript, /GUIDED_ACTION_START_APPLICATION/)
  assert.match(guidedFlowScript, /buildApplicationTemplatePreview/)
  assert.deepEqual(
    buildWelcomeQuickActions(SESSION_TYPE_APPROVAL).map((item) => item.label),
    APPROVAL_WELCOME_QUICK_ACTIONS.map((item) => item.label)
  )
  assert.notDeepEqual(
    buildWelcomeQuickActions(SESSION_TYPE_KNOWLEDGE).map((item) => item.label),
    EXPENSE_WELCOME_QUICK_ACTIONS.map((item) => item.label),
    'knowledge hot questions should stay independent'
  )
})

test('assistant session scope guard keeps business boundaries isolated', () => {
  const expenseInApplication = resolveAssistantScopeGuard('我想报销的士票', SESSION_TYPE_APPLICATION)
  assert.equal(expenseInApplication.targetSessionType, SESSION_TYPE_EXPENSE)
  assert.match(expenseInApplication.text, /申请助手/)
  assert.match(expenseInApplication.text, /报销助手/)
  assert.equal(expenseInApplication.suggestedActions[0].action_type, ASSISTANT_SCOPE_ACTION_SWITCH)
  assert.equal(expenseInApplication.suggestedActions[0].payload.session_type, SESSION_TYPE_EXPENSE)
  assert.equal(expenseInApplication.suggestedActions[0].payload.carry_text, '我想报销的士票')

  assert.equal(resolveAssistantScopeGuard('我想发起一笔费用申请', SESSION_TYPE_APPLICATION), null)
  assert.equal(
    resolveAssistantScopeGuard('去北京出差3天，支撑国网仿生产环境部署', SESSION_TYPE_EXPENSE).targetSessionType,
    SESSION_TYPE_APPLICATION
  )
  assert.equal(
    resolveAssistantScopeGuard('去国网出差3天，协助仿生产环境部署', SESSION_TYPE_EXPENSE).targetSessionType,
    SESSION_TYPE_APPLICATION
  )
  assert.equal(
    resolveAssistantScopeGuard('下周去上海支撑客户系统上线，预计3天', SESSION_TYPE_EXPENSE).targetSessionType,
    SESSION_TYPE_APPLICATION
  )
  assert.equal(
    resolveAssistantScopeGuard('安排去深圳客户现场验收项目', SESSION_TYPE_EXPENSE).targetSessionType,
    SESSION_TYPE_APPLICATION
  )
  assert.equal(
    resolveAssistantScopeGuard('我要报销去北京出差的费用', SESSION_TYPE_APPLICATION).targetSessionType,
    SESSION_TYPE_EXPENSE
  )
  assert.equal(
    resolveAssistantScopeGuard('帮我查询待我审核的单据', SESSION_TYPE_EXPENSE).targetSessionType,
    SESSION_TYPE_APPROVAL
  )
  assert.equal(
    resolveAssistantScopeGuard('差旅住宿标准是多少', SESSION_TYPE_EXPENSE).targetSessionType,
    SESSION_TYPE_KNOWLEDGE
  )
  assert.equal(
    resolveAssistantScopeGuard('报销标准是多少', SESSION_TYPE_EXPENSE).targetSessionType,
    SESSION_TYPE_KNOWLEDGE
  )
  assert.equal(
    resolveAssistantScopeGuard('解释这张单据酒店超标风险', SESSION_TYPE_EXPENSE, { hasActiveReviewPayload: true }),
    null
  )
})

test('guided reimbursement asks type first and walks travel fields in order', () => {
  const typeActions = buildGuidedExpenseTypeActions()
  assert.deepEqual(
    typeActions.map((action) => action.label),
    ['差旅费', '交通费', '住宿费', '业务招待费', '办公用品费', '其他费用']
  )
  assert.ok(typeActions.every((action) => action.action_type === GUIDED_ACTION_SELECT_EXPENSE_TYPE))

  let state = createGuidedReimbursementState()
  assert.equal(state.mode, GUIDED_FLOW_MODE_REIMBURSEMENT)
  assert.equal(state.stepKey, 'expense_type')

  state = selectGuidedExpenseType(state, 'travel')
  assert.equal(state.stepKey, 'reason')
  assert.match(buildGuidedStepPromptText(state), /第 1 步：事由/)

  state = applyGuidedReimbursementAnswer(state, '去上海支持上海电力部署项目')
  assert.equal(state.stepKey, 'location')
  state = applyGuidedReimbursementAnswer(state, '上海')
  assert.equal(state.stepKey, 'time_range')
  state = applyGuidedReimbursementAnswer(state, '2026-05-20 至 2026-05-23，出差 3 天')
  assert.equal(state.stepKey, 'amount')
  state = applyGuidedReimbursementAnswer(state, '待核算')
  assert.equal(state.stepKey, 'attachments')
  state = applyGuidedReimbursementAnswer(state, '稍后上传')

  assert.ok(isGuidedReimbursementReadyForReview(state))
  assert.match(buildGuidedReimbursementSummaryText(state), /已完成“差旅费”的引导填写/)
  assert.deepEqual(
    buildGuidedReviewConfirmationActions().map((action) => action.action_type),
    [GUIDED_ACTION_CONFIRM_REIMBURSEMENT_REVIEW]
  )

  const submitOptions = buildGuidedReviewSubmitOptions(state)
  assert.equal(submitOptions.systemGenerated, true)
  assert.equal(submitOptions.extraContext.expense_scene_selection.expense_type, 'travel')
  assert.equal(submitOptions.extraContext.review_form_values.expense_type, '差旅费')
  assert.match(submitOptions.rawText, /事由：去上海支持上海电力部署项目/)
  assert.match(submitOptions.rawText, /出差时间\/天数：2026-05-20 至 2026-05-23，出差 3 天/)
})

test('guided reimbursement requires application selection for travel and entertainment', () => {
  assert.equal(requiresApplicationBeforeReimbursement('travel'), true)
  assert.equal(requiresApplicationBeforeReimbursement('meal'), true)
  assert.equal(requiresApplicationBeforeReimbursement('transport'), false)

  const claimsPayload = {
    items: [
      {
        id: 'app-travel',
        claim_no: 'AP-202605-001',
        employee_name: '张小青',
        expense_type: 'travel_application',
        reason: '去上海支持项目部署',
        location: '上海',
        amount: 1800,
        occurred_at: '2026-05-20T08:00:00Z',
        status: 'approved',
        created_at: '2026-06-02T00:58:00Z',
        risk_flags_json: [{
          source: 'application_detail',
          application_detail: {
            application_business_time: '2026-05-20 至 2026-05-23',
            days: '4 天',
            transport_mode: '火车',
            lodging_daily_cap: '600元/天',
            subsidy_daily_cap: '120元/天',
            transport_policy: '按真实票据复核',
            policy_estimate: '住宿 2,400元 + 补贴 480元'
          }
        }]
      },
      {
        id: 'app-meal',
        claim_no: 'AP-202605-002',
        employee_name: '张小青',
        expense_type: 'expense_application',
        reason: '客户招待沟通项目',
        location: '武汉',
        amount: 600,
        status: 'approved',
        created_at: '2026-05-21T08:00:00Z'
      },
      {
        id: 'app-draft',
        claim_no: 'AP-202605-003',
        employee_name: '张小青',
        expense_type: 'travel_application',
        reason: '草稿出差申请',
        status: 'draft'
      },
      {
        id: 'app-submitted',
        claim_no: 'AP-202605-005',
        employee_name: '张小青',
        expense_type: 'travel_application',
        reason: '审批中的出差申请',
        status: 'submitted'
      },
      {
        id: 'app-archived-stale-key',
        claim_no: 'AP-202605-007',
        employee_name: '张小青',
        expense_type: 'travel_application',
        reason: '已归档申请单',
        status: 'archived',
        approvalKey: 'completed'
      },
      {
        id: 'app-linked',
        claim_no: 'AP-202605-006',
        employee_name: '张小青',
        expense_type: 'travel_application',
        reason: '已生成报销草稿的出差申请',
        status: 'approved'
      },
      {
        id: 're-linked-draft',
        claim_no: 'RE-202605-006',
        employee_name: '张小青',
        expense_type: 'travel',
        reason: '已关联申请单的报销草稿',
        status: 'draft',
        risk_flags_json: [{
          source: 'application_link',
          application_claim_id: 'app-linked',
          application_claim_no: 'AP-202605-006'
        }]
      },
      {
        id: 'app-other-user',
        claim_no: 'AP-202605-004',
        employee_name: '李四',
        expense_type: 'travel_application',
        reason: '其他员工出差申请',
        status: 'approved'
      }
    ]
  }

  const currentUser = { name: '张小青', username: 'xiaoqing.zhang' }
  const travelApplications = filterRequiredApplicationCandidates(claimsPayload, 'travel', currentUser)
  assert.deepEqual(travelApplications.map((item) => item.claim_no), ['AP-202605-001'])
  assert.match(buildRequiredApplicationSelectionText('travel', travelApplications), /需要先关联对应的申请单/)
  assert.match(buildRequiredApplicationMissingText('meal'), /不能继续这类报销流程/)

  const mealApplications = filterRequiredApplicationCandidates(claimsPayload, 'meal', currentUser)
  assert.deepEqual(mealApplications.map((item) => item.claim_no), ['AP-202605-002'])

  const actions = buildRequiredApplicationActions(travelApplications, GUIDED_ACTION_SELECT_REQUIRED_APPLICATION)
  assert.equal(actions[0].action_type, GUIDED_ACTION_SELECT_REQUIRED_APPLICATION)
  assert.equal(actions[0].payload.application_claim_no, 'AP-202605-001')

  const associationActions = buildReimbursementAssociationActions(travelApplications, '我要报销')
  assert.equal(associationActions[0].action_type, 'select_required_application')
  assert.equal(associationActions[0].payload.application_claim_no, 'AP-202605-001')
  assert.equal(associationActions.at(-1).action_type, SKIP_REQUIRED_APPLICATION_LINK_ACTION)
  assert.match(buildReimbursementAssociationSelectionText(travelApplications), /单独新建报销单/)
  assert.match(buildReimbursementAssociationSelectionText(travelApplications), /ai-document-card-list/)
  assert.match(buildReimbursementAssociationSelectionText(travelApplications), /ai-document-card--application/)
  assert.match(buildReimbursementAssociationMissingText(), /单独新建报销单/)
  const associationQueryPayload = buildReimbursementAssociationQueryPayload(travelApplications)
  assert.equal(associationQueryPayload.selectionMode, 'reimbursement_application_association')
  assert.equal(associationQueryPayload.records[0].claimNo, 'AP-202605-001')
  const completedThinking = buildReimbursementAssociationThinkingEvents('completed', { candidateCount: 1 })
  assert.equal(completedThinking[0].title, '判断用户意图')
  assert.equal(completedThinking.at(-1).status, 'completed')
  const associationSubmitOptions = buildReimbursementAssociationSubmitOptions(
    associationActions[0].payload,
    '我要报销'
  )
  assert.equal(associationSubmitOptions.skipDraftAssociationPrompt, true)
  assert.equal(associationSubmitOptions.extraContext.expense_scene_selection.application_claim_no, 'AP-202605-001')
  assert.equal(associationSubmitOptions.extraContext.review_form_values.application_business_time, '2026-05-20 至 2026-05-23')

  let state = waitForGuidedApplicationSelection(createGuidedReimbursementState(), 'travel', travelApplications)
  assert.equal(state.stepKey, 'application_selection')
  assert.equal(state.applicationCandidates[0].claim_no, 'AP-202605-001')

  state = selectGuidedRequiredApplication(state, actions[0].payload)
  assert.equal(state.stepKey, 'summary')
  assert.equal(isGuidedReimbursementReadyForReview(state), true)
  assert.equal(state.values.application_claim_no, 'AP-202605-001')
  assert.equal(state.values.application_business_time, '2026-05-20 至 2026-05-23')
  assert.equal(state.values.application_days, '4 天')
  assert.equal(state.values.application_transport_mode, '火车')
  assert.equal(state.values.application_lodging_daily_cap, '600元/天')
  assert.equal(state.values.application_subsidy_daily_cap, '120元/天')
  const summaryText = buildGuidedReimbursementSummaryText(state)
  assert.match(summaryText, /关联申请单：AP-202605-001/)
  assert.match(summaryText, /草稿详情中上传对应票据/)
  assert.doesNotMatch(summaryText, /事由：待补充/)

  const submitOptions = buildGuidedReviewSubmitOptions(state)
  assert.equal(submitOptions.extraContext.review_action, 'save_draft')
  assert.equal(submitOptions.extraContext.review_form_values.application_claim_no, 'AP-202605-001')
  assert.equal(submitOptions.extraContext.review_form_values.reason, '去上海支持项目部署')
  assert.equal(submitOptions.extraContext.review_form_values.location, '上海')
  assert.equal(submitOptions.extraContext.review_form_values.amount, '')
  assert.equal(submitOptions.extraContext.review_form_values.application_amount, '1800')
  assert.equal(submitOptions.extraContext.review_form_values.application_business_time, '2026-05-20 至 2026-05-23')
  assert.equal(submitOptions.extraContext.review_form_values.application_days, '4 天')
  assert.equal(submitOptions.extraContext.review_form_values.transport_mode, '火车')
  assert.equal(submitOptions.extraContext.review_form_values.application_transport_mode, '火车')
  assert.equal(submitOptions.extraContext.review_form_values.reimbursement_type, undefined)
  assert.equal(submitOptions.extraContext.review_form_values.reason_value, undefined)
  assert.equal(submitOptions.extraContext.review_form_values.business_time, undefined)
  assert.equal(submitOptions.extraContext.review_form_values.business_location, undefined)
  assert.equal(submitOptions.extraContext.review_form_values.application_lodging_daily_cap, '600元/天')
  assert.equal(submitOptions.extraContext.review_form_values.application_subsidy_daily_cap, '120元/天')
  assert.equal(submitOptions.extraContext.expense_scene_selection.application_claim_no, 'AP-202605-001')
  assert.match(submitOptions.rawText, /关联申请单：AP-202605-001/)
  assert.doesNotMatch(submitOptions.rawText, /事由：待补充/)
})

test('guided reimbursement interrupts suspicious questions before expensive flow', () => {
  const state = selectGuidedExpenseType(createGuidedReimbursementState(), 'transport')
  assert.equal(shouldConfirmGuidedInterruption('送客户去机场', state), false)
  assert.equal(shouldConfirmGuidedInterruption('帮我查询一下上周的报销状态？', state), true)
  assert.deepEqual(
    buildGuidedInterruptionActions().map((action) => action.action_type),
    [GUIDED_ACTION_CONTINUE_FILLING, GUIDED_ACTION_PROCESS_INTERRUPTION]
  )
})

test('status query guide collects a query mode before calling existing query flow', () => {
  let state = createGuidedStatusQueryState()
  assert.equal(state.mode, GUIDED_FLOW_MODE_STATUS_QUERY)
  assert.equal(state.stepKey, 'query_mode')
  assert.ok(buildGuidedQueryModeActions().every((action) => action.action_type === GUIDED_ACTION_SELECT_QUERY_MODE))

  state = selectGuidedQueryMode(state, 'status')
  assert.equal(state.stepKey, 'status_value')
  assert.ok(buildGuidedQueryStatusActions().every((action) => action.action_type === GUIDED_ACTION_SELECT_QUERY_STATUS))
  assert.equal(buildGuidedStatusQueryText(state, '已归档'), '帮我查询已归档的报销单据，筛选最近的 5 条记录')

  const keywordState = selectGuidedQueryMode(createGuidedStatusQueryState(), 'keyword')
  assert.equal(keywordState.stepKey, 'query_value')
  assert.equal(
    buildGuidedStatusQueryText(keywordState, '上海电力'),
    '帮我查询地点或事由包含“上海电力”的报销单据状态，筛选最近的 5 条记录'
  )
})

test('guided flow state is serializable and restored through session state', () => {
  const empty = createEmptyGuidedFlowState()
  assert.deepEqual(normalizeGuidedFlowState({ mode: 'bad' }), empty)
  assert.deepEqual(
    normalizeGuidedFlowState({
      mode: GUIDED_FLOW_MODE_REIMBURSEMENT,
      stepKey: 'amount',
      expenseType: 'travel',
      values: {
        amount: 200,
        attachment_names: ['a.pdf', '', 'a.pdf']
      },
      pendingInterruptionText: '查询状态？'
    }),
    {
      mode: GUIDED_FLOW_MODE_REIMBURSEMENT,
      stepKey: 'amount',
      expenseType: 'travel',
      values: {
        amount: '200',
        attachment_names: ['a.pdf']
      },
      pendingInterruptionText: '查询状态？',
      applicationCandidates: []
    }
  )

  assert.match(sessionStateScript, /guidedFlowState:\s*normalizeGuidedFlowState\(state\.guidedFlowState\)/)
  assert.match(sessionStateScript, /runtimeRefs\.guidedFlowState\?\.value/)
  assert.match(sessionStateScript, /guidedFlowState,\s*\n\s*insightPanelCollapsed/)
  assert.match(sessionStateScript, /function refreshWelcomeQuickActions/)
  assert.match(sessionStateScript, /buildWelcomeQuickActions\(/)
  assert.match(sessionStateScript, /resolveAccessibleSessionTypes\(\)\.reduce/)
  assert.match(sessionStateScript, /props\.entrySource === 'application' \? SESSION_TYPE_APPLICATION : SESSION_TYPE_EXPENSE/)
  assert.match(sessionStateScript, /const canRestorePersistedInitialState =[\s\S]*shouldPersistLocalSnapshot/)
})

test('guided flow is local until final confirmation or collected query handoff', () => {
  assert.doesNotMatch(guidedFlowScript, /runOrchestrator/)
  assert.doesNotMatch(guidedFlowScript, /startExpenseClaimDraftFlowStep/)
  assert.match(guidedModelScript, /review_action:\s*['"]save_draft['"]/)
  assert.match(guidedFlowScript, /fetchExpenseClaims/)
  assert.match(guidedFlowScript, /REIMBURSEMENT_LIST_PREVIEW_PARAMS/)
  assert.match(guidedFlowScript, /fetchExpenseClaims\(REIMBURSEMENT_LIST_PREVIEW_PARAMS\)/)
  assert.match(submitDraftPreflightScript, /REIMBURSEMENT_LIST_PREVIEW_PARAMS/)
  assert.match(submitDraftPreflightScript, /fetchExpenseClaims\(REIMBURSEMENT_LIST_PREVIEW_PARAMS\)/)
  assert.doesNotMatch(guidedFlowScript, /fetchExpenseClaims\(\)/)
  assert.doesNotMatch(submitDraftPreflightScript, /fetchExpenseClaims\(\)/)
  assert.match(guidedFlowScript, /GUIDED_ACTION_SELECT_REQUIRED_APPLICATION/)
  assert.match(guidedFlowScript, /isGuidedReimbursementReadyForReview\(guidedFlowState\.value\)[\s\S]*pushReimbursementSummary\(\)/)
  assert.match(guidedFlowScript, /isGuidedReimbursementReadyForReview\(currentState\) && fileNames\.length[\s\S]*buildGuidedReviewSubmitOptions\(currentState, mergedFiles\)[\s\S]*skipDraftAssociationPrompt:\s*true[\s\S]*skipUserMessage:\s*true[\s\S]*submitExistingComposer\(submitOptions\)/)
  assert.doesNotMatch(guidedFlowScript, /amount:\s*applicationAmount/)
  assert.match(guidedFlowScript, /amount:\s*''/)
  assert.match(guidedFlowScript, /if \(!applications\.length\) \{[\s\S]*guidedFlowState\.value = createEmptyGuidedFlowState\(\)[\s\S]*meta: \['缺少可关联申请单'\][\s\S]*\}\)/)
  assert.doesNotMatch(guidedFlowScript, /meta: \['缺少可关联申请单'\],[\s\S]{0,120}suggestedActions: buildGuidedExpenseTypeActions\(\)/)
  assert.match(guidedFlowScript, /handleSceneSelectionApplicationGate/)
  assert.match(createViewScript, /handleSceneSelectionApplicationGate/)
  assert.match(createViewScript, /submitComposerFromMessageHandlers/)
  assert.match(messageHandlersScript, /if \(await handleGuidedComposerSubmit\(options\)\) return null[\s\S]*return submitComposerInternal\(options\)/)
  assert.match(suggestedActionsScript, /ASSISTANT_SCOPE_ACTION_SWITCH/)
  assert.match(suggestedActionsScript, /actionPayload\.carry_text/)
  assert.match(suggestedActionsScript, /targetSessionType === SESSION_TYPE_EXPENSE[\s\S]*carryText === '我要报销'[\s\S]*pushExpenseAssociationGatePrompt\(carryText\)/)
  assert.match(suggestedActionsScript, /actionType === SKIP_REQUIRED_APPLICATION_LINK_ACTION[\s\S]*pushExpenseSceneSelectionPrompt/)
  assert.match(suggestedActionsScript, /actionType === 'confirm_expense_intent'[\s\S]*pushExpenseAssociationGatePrompt\(originalMessage\)/)
  assert.match(suggestedActionsScript, /pushReimbursementAssociationPromptMessage\(\{[\s\S]*skipDraftCheck: Boolean\(options\.skipDraftCheck\)/)
  assert.match(submitComposerScript, /waitForExpenseSceneSelection[\s\S]*pushReimbursementAssociationPromptMessage\(\{[\s\S]*rawText/)
  assert.match(submitComposerScript, /fetchExpenseClaims[\s\S]*currentUser/)
  assert.doesNotMatch(submitComposerScript, /if \(waitForExpenseSceneSelection\) \{[\s\S]{0,260}buildExpenseSceneSelectionMessage/)
  assert.match(submitComposerScript, /resolveAssistantScopeGuard/)
  assert.match(submitComposerScript, /skipScopeGuard/)
  assert.match(guidedFlowScript, /submitExistingComposer\(submitOptions\)/)
  assert.match(guidedFlowScript, /submitExistingComposer\(\{[\s\S]*pendingText:\s*'正在查询单据状态\.\.\.'/)
})
