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

import {
  ATTACHMENT_ASSOCIATION_CONFIRM_HREF,
  buildAttachmentAssociationConfirmationMessage,
  buildOcrFilePreviews,
  buildReviewFilePreviewsFromReviewPayload,
  buildUnsavedDraftAttachmentConfirmationMessage,
  filterPersistableFilePreviews,
  mergeFilePreviews
} from '../src/views/scripts/travelReimbursementAttachmentModel.js'
import {
  buildDraftAssociationQueryPayload,
  buildExpenseQueryHint,
  EXPENSE_CENTER_HREF,
  normalizeExpenseQueryPayload
} from '../src/views/scripts/travelReimbursementExpenseQueryModel.js'
import { renderMarkdown } from '../src/utils/markdown.js'

test('attachment association prompt prints recognized receipt details before confirmation link', () => {
  const message = buildAttachmentAssociationConfirmationMessage({
    claimNo: 'EXP-202605-001',
    fileNames: ['train-ticket.pdf'],
    ocrDocuments: [
      {
        filename: 'train-ticket.pdf',
        document_type: 'train_ticket',
        scene_label: '差旅票据',
        summary: '铁路电子客票 武汉-上海 票价 354 元',
        document_fields: [
          { key: 'route', label: '行程', value: '武汉-上海' },
          { key: 'amount', label: '票价', value: '354.00' },
          { key: 'date', label: '乘车日期', value: '2026-02-20' }
        ]
      }
    ]
  })

  assert.match(message, /已识别附件信息：/)
  assert.match(message, /> \*\*附件 1：train-ticket\.pdf\*\*/)
  assert.match(message, /附件类型：差旅票据/)
  assert.match(message, /行程：武汉-上海/)
  assert.match(message, /票价：354.00/)
  assert.match(message, /草稿单号：EXP-202605-001/)
  assert.match(message, new RegExp(`\\n\\n\\n如果 \\*\\*\\[确认\\]\\(${ATTACHMENT_ASSOCIATION_CONFIRM_HREF}\\)\\*\\* 该信息`))

  const rendered = renderMarkdown(message)
  assert.match(rendered, /<blockquote class="markdown-attachment-card">/)
  const questionIndex = rendered.indexOf('请问是否确定将票据信息归集到单据')
  const attachmentCardCloseIndex = rendered.indexOf('</blockquote>')
  assert.ok(attachmentCardCloseIndex > -1 && questionIndex > attachmentCardCloseIndex)
  const attachmentCardHtml = rendered.slice(
    rendered.indexOf('<blockquote class="markdown-attachment-card">'),
    attachmentCardCloseIndex
  )
  assert.doesNotMatch(attachmentCardHtml, /请问是否确定将票据信息归集到单据/)
  assert.match(rendered, /<p class="markdown-action-paragraph">/)
  assert.match(rendered, /<strong><a href="#confirm-attachment-association" class="markdown-action-link markdown-action-link-confirm">确认<\/a><\/strong>/)
})

test('multiple recognized attachments render as separated attachment cards', () => {
  const message = buildAttachmentAssociationConfirmationMessage({
    claimNo: 'EXP-202605-001',
    ocrDocuments: [
      {
        filename: '2月20 武汉-上海.pdf',
        document_type: 'train_ticket',
        document_type_label: '火车/高铁票',
        document_fields: [
          { key: 'amount', label: '金额', value: '354元' },
          { key: 'route', label: '行程', value: '武汉-上海' }
        ]
      },
      {
        filename: '2月23 上海-武汉.pdf',
        document_type: 'train_ticket',
        document_type_label: '火车/高铁票',
        document_fields: [
          { key: 'amount', label: '金额', value: '354元' },
          { key: 'route', label: '行程', value: '上海-武汉' }
        ]
      }
    ]
  })
  const rendered = renderMarkdown(message)

  assert.equal((rendered.match(/class="markdown-attachment-card"/g) || []).length, 2)
  assert.match(rendered, /<strong>附件 1：2月20 武汉-上海\.pdf<\/strong>/)
  assert.match(rendered, /<strong>附件 2：2月23 上海-武汉\.pdf<\/strong>/)
  assert.match(rendered, /本次待归集附件：2 份/)
})

test('attachment upload association uses conversation selection instead of legacy modal', () => {
  const viewSource = readFileSync(
    fileURLToPath(new URL('../src/views/TravelReimbursementCreateView.vue', import.meta.url)),
    'utf8'
  )
  const submitComposerSource = readFileSync(
    fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementSubmitComposer.js', import.meta.url)),
    'utf8'
  )
  const flowSource = readFileSync(
    fileURLToPath(new URL('../src/views/scripts/useTravelReimbursementFlow.js', import.meta.url)),
    'utf8'
  )
  const conversationSource = readFileSync(
    fileURLToPath(new URL('../src/views/scripts/travelReimbursementConversationModel.js', import.meta.url)),
    'utf8'
  )

  assert.doesNotMatch(viewSource, /检测到你已有单据事件|uploadDecisionDialogOpen|continueExistingUpload|createNewUploadDocument/)
  assert.doesNotMatch(submitComposerSource, /uploadDecisionDialogOpen|hasExistingDocumentEvent|skipUploadDecisionPrompt/)
  assert.doesNotMatch(submitComposerSource, /查询可关联草稿失败，已继续按新单据识别/)
  assert.match(
    submitComposerSource,
    /const claims = await fetchExpenseClaims\(\)[\s\S]*const queryPayload = buildDraftAssociationQueryPayload\(claims\)[\s\S]*meta: \['等待选择关联单据'\][\s\S]*queryPayload/
  )
  assert.match(submitComposerSource, /meta: \['单据查询失败'\][\s\S]*return null/)
  assert.match(
    submitComposerSource,
    /files\.length[\s\S]*!resolvedUploadDisposition[\s\S]*!options\.skipDraftAssociationPrompt[\s\S]*!reviewAction/
  )
  assert.match(submitComposerSource, /mode:\s*'save_then_associate'/)
  assert.match(submitComposerSource, /review_action:\s*'save_draft'[\s\S]*review_action:\s*'link_to_existing_draft'/)
  assert.match(submitComposerSource, /appendToCurrentFlow:\s*true/)
  assert.match(submitComposerSource, /const appendToCurrentFlow = Boolean\(options\.appendToCurrentFlow\)/)
  assert.match(submitComposerSource, /if \(!appendToCurrentFlow\) \{\s*resetFlowRun\(\)\s*\} else \{\s*clearFlowSimulationTimers\(\)/)
  assert.match(flowSource, /link_to_existing_draft:\s*\{[\s\S]*key:\s*'attachment-association'/)
  assert.match(flowSource, /responseMessage\.includes\('关联'\)[\s\S]*key:\s*'attachment-association'/)
  assert.match(conversationSource, /'attachment-association':\s*\{[\s\S]*title:\s*'票据关联草稿'/)
})

test('unsaved review attachment prompt asks for explicit rich-text confirmation', () => {
  const message = buildUnsavedDraftAttachmentConfirmationMessage({
    fileNames: ['taxi.pdf']
  })
  const rendered = renderMarkdown(message)

  assert.match(message, /当前这笔报销信息还没有保存为草稿/)
  assert.match(message, /本次待归集附件：1 份/)
  assert.match(message, new RegExp(`\\*\\*\\[确定\\]\\(${ATTACHMENT_ASSOCIATION_CONFIRM_HREF}\\)\\*\\*`))
  assert.match(rendered, /<strong><a href="#confirm-attachment-association" class="markdown-action-link markdown-action-link-confirm">确定<\/a><\/strong>/)
})

test('OCR preview builders keep hotel receipt image previews when preview kind is omitted', () => {
  const dataUrl = 'data:image/png;base64,abc123'
  const ocrPreviews = buildOcrFilePreviews({
    documents: [
      {
        filename: 'hotel.png',
        preview_data_url: dataUrl
      }
    ]
  })
  const reviewPreviews = buildReviewFilePreviewsFromReviewPayload({
    document_cards: [
      {
        filename: 'hotel.png',
        preview_url: dataUrl
      }
    ]
  })

  assert.deepEqual(ocrPreviews, [{ filename: 'hotel.png', kind: 'image', url: dataUrl }])
  assert.deepEqual(reviewPreviews, [{ filename: 'hotel.png', kind: 'image', url: dataUrl }])
})

test('file preview cache replaces temporary object urls and never persists them', () => {
  const merged = mergeFilePreviews(
    [
      { filename: 'invoice.pdf', kind: 'pdf', url: 'blob:http://localhost/old-preview' },
      { filename: 'hotel.png', kind: 'image', url: 'data:image/png;base64,stable' }
    ],
    [
      { filename: 'invoice.pdf', kind: 'pdf', url: 'blob:http://localhost/new-preview' },
      { filename: 'hotel.png', kind: 'image' }
    ]
  )

  assert.equal(merged.length, 2)
  assert.equal(merged[0].url, 'blob:http://localhost/new-preview')
  assert.equal(merged[1].url, 'data:image/png;base64,stable')
  assert.deepEqual(filterPersistableFilePreviews(merged), [
    { filename: 'hotel.png', kind: 'image', url: 'data:image/png;base64,stable' }
  ])
})

test('draft association query keeps a single candidate selectable in the conversation', () => {
  const payload = buildDraftAssociationQueryPayload([
    {
      id: 'claim-1',
      claim_no: 'EXP-202605-001',
      status: 'draft',
      expense_type: 'travel',
      reason: '上海出差',
      amount: 1280
    }
  ])

  assert.equal(payload.selectionMode, 'draft_association')
  assert.equal(payload.title, '选择关联草稿')
  assert.equal(payload.records.length, 1)
  assert.equal(payload.records[0].claimId, 'claim-1')
})

test('expense query payload keeps structured risk items for claim-level risk drilldown', () => {
  const payload = normalizeExpenseQueryPayload({
    result_type: 'expense_claim_list',
    records: [
      {
        claim_id: 'claim-risk',
        claim_no: 'EXP-202605-009',
        amount: 880,
        risk_flags: [
          {
            key: 'hotel-limit',
            level: 'high',
            level_label: '高风险',
            title: '酒店超标',
            summary: '住宿金额超过城市标准',
            detail: '上海 P5 住宿标准为 600 元，本次 880 元。'
          }
        ]
      }
    ]
  })

  assert.equal(payload.records[0].riskItems.length, 1)
  assert.equal(payload.records[0].riskItems[0].levelLabel, '高风险')
  assert.equal(payload.records[0].riskItems[0].summary, '住宿金额超过城市标准')
})

test('expense query info items render as prompts instead of low risk', () => {
  const payload = normalizeExpenseQueryPayload({
    result_type: 'expense_claim_list',
    records: [
      {
        claim_id: 'claim-info',
        claim_no: 'EXP-202605-010',
        amount: 59.1,
        risk_flags: [
          {
            key: 'normal-tip',
            level: 'info',
            title: '票据提示',
            summary: '票据已识别，当前没有异常。'
          }
        ]
      }
    ]
  })

  assert.equal(payload.records[0].riskItems[0].levelLabel, '提示')
  assert.notEqual(payload.records[0].riskItems[0].levelLabel, '低风险')
})

test('expense query hint guides users to the document center after the top five results', () => {
  const payload = normalizeExpenseQueryPayload({
    result_type: 'expense_claim_list',
    title: '最近 5 条你的归档报销单',
    scope_label: '你的归档报销单',
    record_count: 8,
    preview_count: 5,
    preview_limit: 5,
    records: [
      { claim_id: 'claim-1', claim_no: 'EXP-1', amount: 100 }
    ]
  })
  const hint = buildExpenseQueryHint(payload)

  assert.match(hint, /最近的 5 条记录/)
  assert.match(hint, new RegExp(`\\[\\*\\*这里\\*\\*\\]\\(${EXPENSE_CENTER_HREF}\\)`))
})
