import test from 'node:test'
import assert from 'node:assert/strict'

import {
  buildAttachmentAssociationCandidateActions,
  normalizeAttachmentAssociationJob,
  resolveAttachmentAssociationTarget
} from '../src/utils/attachmentAssociationJobModel.js'
import {
  buildAiAttachmentAssociationConfirmationResultMessage,
  buildAiAttachmentAssociationResultMessage
} from '../src/utils/aiAttachmentAssociationModel.js'
import { renderAiConversationHtml } from '../src/utils/aiConversationHtmlRenderer.js'
import { createWorkbenchAiMessageRuntime } from '../src/composables/workbenchAiMode/workbenchAiMessageModel.js'
import { useWorkbenchAiAttachmentAssociationJobs } from '../src/composables/workbenchAiMode/useWorkbenchAiAttachmentAssociationJobs.js'

function buildConfirmationJob(overrides = {}) {
  return {
    job_id: 'association-job-1',
    status: 'succeeded',
    resolution: 'requires_confirmation',
    requires_confirmation: true,
    expense_case_id: 'case-1',
    application_claim_id: 'application-1',
    application_claim_no: 'SQ-20260716-001',
    confidence: 'medium',
    confidence_score: 0.8,
    match_reasons: ['日期接近', '行程城市相同'],
    exceptions: [{ code: 'ambiguous_candidates', message: '前两名候选分差过小' }],
    missing_fields: ['项目待补充'],
    risk_items: [{ code: 'amount_gap', description: '票据金额与申请预算存在差异' }],
    candidates: [
      {
        target_type: 'reimbursement_draft',
        expense_case_id: 'case-1',
        application_claim_id: 'application-1',
        application_claim_no: 'SQ-20260716-001',
        claim_id: 'draft-1',
        claim_no: 'BX-20260716-001',
        confidence: 'medium',
        score: 0.8,
        match_reasons: ['日期接近', '行程城市相同']
      },
      {
        target_type: 'reimbursement_draft',
        expense_case_id: 'case-2',
        claim_id: 'draft-2',
        claim_no: 'BX-20260716-002',
        confidence: 'medium',
        score: 0.67,
        match_reasons: ['日期接近']
      }
    ],
    draft_payload: { claim_id: 'draft-preview', claim_no: 'BX-PREVIEW-001' },
    ...overrides
  }
}

test('附件关联任务完整规范化新协议并在会话序列化后保留', () => {
  const normalized = normalizeAttachmentAssociationJob(buildConfirmationJob())

  assert.equal(normalized.requiresConfirmation, true)
  assert.equal(normalized.expenseCaseId, 'case-1')
  assert.equal(normalized.applicationClaimNo, 'SQ-20260716-001')
  assert.equal(normalized.confidenceScore, 0.8)
  assert.deepEqual(normalized.matchReasons, ['日期接近', '行程城市相同'])
  assert.equal(normalized.exceptions[0].code, 'ambiguous_candidates')
  assert.equal(normalized.missingFields[0].message, '项目待补充')
  assert.equal(normalized.riskItems[0].message, '票据金额与申请预算存在差异')
  assert.equal(normalized.candidates[0].claimId, 'draft-1')
  assert.equal(normalized.draftPayload.claimNo, 'BX-PREVIEW-001')

  const runtime = createWorkbenchAiMessageRuntime()
  const message = runtime.createInlineMessage('assistant', '后台处理中', {
    attachmentAssociationJob: buildConfirmationJob()
  })
  const restored = runtime.normalizeRuntimeMessage(runtime.serializeRuntimeMessage(message))

  assert.deepEqual(restored.attachmentAssociationJob, normalized)
})

test('待确认结果集中展示候选和异常且不伪装成自动归集成功', () => {
  const job = normalizeAttachmentAssociationJob(buildConfirmationJob())
  const message = buildAiAttachmentAssociationConfirmationResultMessage({
    job,
    fileNames: ['武汉-上海.pdf']
  })
  const html = renderAiConversationHtml(message)
  const actions = buildAttachmentAssociationCandidateActions(job)

  assert.match(message, /当前信息不足以安全自动归集/)
  assert.match(html, /候选单据需要确认/)
  assert.match(html, /BX-20260716-001/)
  assert.match(html, /前两名候选分差过小/)
  assert.match(html, /项目待补充/)
  assert.match(html, /票据金额与申请预算存在差异/)
  assert.match(html, /没有写入任何草稿/)
  assert.doesNotMatch(message, /已完成自动归集/)
  assert.equal(actions.length, 2)
  assert.equal(actions[0].label, '查看候选草稿 1')
  assert.equal(actions[0].payload.claim_id, 'draft-1')
  assert.equal(actions[0].payload.application_claim_no, 'SQ-20260716-001')
})

test('只有申请候选时保留申请详情动作且不会伪造报销草稿', () => {
  const job = normalizeAttachmentAssociationJob(buildConfirmationJob({
    candidates: [{
      target_type: 'approved_application',
      expense_case_id: 'case-application-only',
      application_claim_id: 'application-only',
      application_claim_no: 'AP-ONLY-001',
      confidence: 'high',
      score: 0.8,
      match_reasons: ['申请日期和票据日期一致']
    }]
  }))
  const actions = buildAttachmentAssociationCandidateActions(job)

  assert.equal(job.candidates.length, 1)
  assert.equal(job.candidates[0].claimId, '')
  assert.equal(actions[0].label, '查看候选申请')
  assert.equal(actions[0].payload.claim_id, 'application-only')
  assert.equal(actions[0].payload.document_type, 'application')
})

test('幂等重放展示为已关联而不是未归集', () => {
  const message = buildAiAttachmentAssociationResultMessage({
    claimNo: 'BX-20260716-001',
    uploadedCount: 0,
    skippedCount: 1,
    fileNames: ['武汉-上海.pdf']
  })

  assert.match(message, /此前已经完成归集/)
  assert.match(message, /1 份已关联，无需重复归集/)
  assert.doesNotMatch(message, /未归集附件|部分完成/)
})

test('自动归集成功仍展示草稿风险和复核提示', () => {
  const message = buildAiAttachmentAssociationResultMessage({
    claimNo: 'BX-20260716-001',
    uploadedCount: 1,
    riskItems: [{ message: '票据金额需要人工复核' }],
    confirmationRequired: true
  })

  assert.match(message, /风险提示/)
  assert.match(message, /票据金额需要人工复核/)
  assert.match(message, /待复核/)
  assert.match(message, /复核后再提交/)
})

test('任务编排把待确认结果视为安全终态且不发送归集成功通知', async () => {
  const runtime = createWorkbenchAiMessageRuntime()
  const conversationMessages = {
    value: [runtime.createInlineMessage('assistant', '后台处理中', { id: 'message-1', pending: true })]
  }
  let notifyCount = 0
  let persistCount = 0
  const flow = useWorkbenchAiAttachmentAssociationJobs({
    conversationMessages,
    createInlineMessage: runtime.createInlineMessage,
    persistCurrentConversation: () => { persistCount += 1 },
    replaceInlineMessage: (messageId, message) => {
      const index = conversationMessages.value.findIndex((item) => item.id === messageId)
      conversationMessages.value.splice(index, 1, message)
    },
    streamOrSetInlineAssistantContent: async () => {},
    notifyRequestUpdated: () => { notifyCount += 1 },
    toast: () => {},
    buildDetailActions: () => [{ label: '查看草稿' }],
    buildThinkingEvents: (status) => [{ status }]
  })

  const done = await flow.updateJobMessage({
    job: buildConfirmationJob(),
    messageId: 'message-1',
    fileNames: ['武汉-上海.pdf']
  })
  const resultMessage = conversationMessages.value[0]

  assert.equal(done, true)
  assert.equal(notifyCount, 0)
  assert.equal(persistCount, 1)
  assert.equal(resultMessage.pending, false)
  assert.equal(resultMessage.stewardPlan.streamStatus, 'completed')
  assert.match(resultMessage.content, /未归集/)
  assert.doesNotMatch(resultMessage.content, /已完成自动归集/)
  assert.equal(resultMessage.suggestedActions[0].payload.claim_id, 'draft-1')
})

test('自动完成结果可从 draft_payload 恢复并继续提供查看草稿动作', async () => {
  const runtime = createWorkbenchAiMessageRuntime()
  const conversationMessages = {
    value: [runtime.createInlineMessage('assistant', '后台处理中', { id: 'message-2', pending: true })]
  }
  let notification = null
  const flow = useWorkbenchAiAttachmentAssociationJobs({
    conversationMessages,
    createInlineMessage: runtime.createInlineMessage,
    persistCurrentConversation: () => {},
    replaceInlineMessage: (messageId, message) => {
      const index = conversationMessages.value.findIndex((item) => item.id === messageId)
      conversationMessages.value.splice(index, 1, message)
    },
    streamOrSetInlineAssistantContent: async () => {},
    notifyRequestUpdated: (payload) => { notification = payload },
    toast: () => {},
    buildDetailActions: (target) => [{
      label: '查看草稿',
      action_type: 'open_application_detail',
      payload: { claim_id: target.claimId, claim_no: target.claimNo }
    }],
    buildThinkingEvents: (status) => [{ status }]
  })
  const job = {
    job_id: 'association-job-2',
    status: 'succeeded',
    resolution: 'auto_associated',
    uploaded_count: 1,
    skipped_count: 0,
    draft_payload: { claim_id: 'draft-auto', claim_no: 'BX-AUTO-001' }
  }

  await flow.updateJobMessage({ job, messageId: 'message-2', fileNames: ['住宿发票.pdf'] })
  const resultMessage = conversationMessages.value[0]

  assert.deepEqual(resolveAttachmentAssociationTarget(job), {
    claimId: 'draft-auto',
    claimNo: 'BX-AUTO-001'
  })
  assert.match(resultMessage.content, /已完成自动归集/)
  assert.equal(resultMessage.suggestedActions[0].label, '查看草稿')
  assert.equal(resultMessage.suggestedActions[0].payload.claim_id, 'draft-auto')
  assert.equal(notification.claimId, 'draft-auto')
})
