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

function normalizeStringList(values = []) {
  return (Array.isArray(values) ? values : [])
    .map((item) => normalizeText(item))
    .filter(Boolean)
}

function normalizeIssue(item = null) {
  if (!item) {
    return null
  }
  if (typeof item === 'string' || typeof item === 'number') {
    const message = normalizeText(item)
    return message ? { code: '', message } : null
  }
  if (typeof item !== 'object') {
    return null
  }
  const code = normalizeText(item.code || item.type || item.key)
  const message = normalizeText(
    item.message || item.detail || item.description || item.reason || item.label || code
  )
  return message ? { code, message } : null
}

function normalizeIssueList(values = []) {
  return (Array.isArray(values) ? values : [])
    .map((item) => normalizeIssue(item))
    .filter(Boolean)
}

function normalizeDraftPayload(payload = null) {
  if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
    return null
  }
  return {
    ...payload,
    claimId: normalizeText(payload.claimId || payload.claim_id || payload.id),
    claimNo: normalizeText(payload.claimNo || payload.claim_no)
  }
}

export function normalizeAttachmentAssociationCandidate(candidate = null) {
  if (!candidate || typeof candidate !== 'object') {
    return null
  }
  const claimId = normalizeText(candidate.claimId || candidate.claim_id)
  const claimNo = normalizeText(candidate.claimNo || candidate.claim_no)
  const applicationClaimId = normalizeText(candidate.applicationClaimId || candidate.application_claim_id)
  const applicationClaimNo = normalizeText(candidate.applicationClaimNo || candidate.application_claim_no)
  if (!claimId && !claimNo && !applicationClaimId && !applicationClaimNo) {
    return null
  }
  return {
    targetType: normalizeText(candidate.targetType || candidate.target_type || 'expense_claim'),
    expenseCaseId: normalizeText(candidate.expenseCaseId || candidate.expense_case_id),
    applicationClaimId,
    applicationClaimNo,
    claimId,
    claimNo,
    confidence: normalizeText(candidate.confidence),
    score: Number(candidate.score ?? candidate.confidence_score ?? 0) || 0,
    matchReasons: normalizeStringList(candidate.matchReasons || candidate.match_reasons || [])
  }
}

export function normalizeAttachmentAssociationJob(job = null) {
  if (!job || typeof job !== 'object') {
    return null
  }
  const jobId = normalizeText(job.jobId || job.job_id)
  if (!jobId) {
    return null
  }
  const resolution = normalizeText(job.resolution)
  const requiresConfirmation = Boolean(
    job.requiresConfirmation ?? job.requires_confirmation ?? resolution === 'requires_confirmation'
  )
  return {
    jobId,
    status: normalizeText(job.status || 'queued') || 'queued',
    resolution: resolution || (requiresConfirmation ? 'requires_confirmation' : ''),
    requiresConfirmation,
    message: normalizeText(job.message),
    receiptIds: normalizeStringList(job.receiptIds || job.receipt_ids || []),
    expenseCaseId: normalizeText(job.expenseCaseId || job.expense_case_id),
    applicationClaimId: normalizeText(job.applicationClaimId || job.application_claim_id),
    applicationClaimNo: normalizeText(job.applicationClaimNo || job.application_claim_no),
    claimId: normalizeText(job.claimId || job.claim_id),
    claimNo: normalizeText(job.claimNo || job.claim_no),
    confidence: normalizeText(job.confidence),
    confidenceScore: Number(job.confidenceScore ?? job.confidence_score ?? 0) || 0,
    matchReasons: normalizeStringList(job.matchReasons || job.match_reasons || []),
    exceptions: normalizeIssueList(job.exceptions || []),
    missingFields: normalizeIssueList(job.missingFields || job.missing_fields || []),
    riskItems: normalizeIssueList(job.riskItems || job.risk_items || []),
    candidates: (Array.isArray(job.candidates) ? job.candidates : [])
      .map((candidate) => normalizeAttachmentAssociationCandidate(candidate))
      .filter(Boolean),
    draftPayload: normalizeDraftPayload(job.draftPayload || job.draft_payload),
    uploadedCount: Number(job.uploadedCount ?? job.uploaded_count ?? 0) || 0,
    skippedCount: Number(job.skippedCount ?? job.skipped_count ?? 0) || 0,
    error: normalizeText(job.error)
  }
}

export function isAttachmentAssociationConfirmationResult(job = null) {
  const normalizedJob = normalizeAttachmentAssociationJob(job)
  return Boolean(
    normalizedJob?.requiresConfirmation || normalizedJob?.resolution === 'requires_confirmation'
  )
}

export function resolveAttachmentAssociationTarget(job = null) {
  const normalizedJob = normalizeAttachmentAssociationJob(job)
  if (!normalizedJob) {
    return { claimId: '', claimNo: '' }
  }
  const draftPayload = normalizedJob.draftPayload || {}
  const firstCandidate = normalizedJob.candidates[0] || {}
  return {
    claimId: normalizeText(
      normalizedJob.claimId || draftPayload.claimId || draftPayload.claim_id || draftPayload.id || firstCandidate.claimId
    ),
    claimNo: normalizeText(
      normalizedJob.claimNo || draftPayload.claimNo || draftPayload.claim_no || firstCandidate.claimNo
    )
  }
}

export function buildAttachmentAssociationCandidateActions(job = null) {
  const normalizedJob = normalizeAttachmentAssociationJob(job)
  if (!normalizedJob) {
    return []
  }
  return normalizedJob.candidates.slice(0, 3).map((candidate, index) => {
    const applicationOnly = !candidate.claimId && !candidate.claimNo && Boolean(
      candidate.applicationClaimId || candidate.applicationClaimNo
    )
    const labelPrefix = applicationOnly ? '查看候选申请' : '查看候选草稿'
    const documentNo = applicationOnly ? candidate.applicationClaimNo : candidate.claimNo
    return {
      label: normalizedJob.candidates.length > 1 ? `${labelPrefix} ${index + 1}` : labelPrefix,
      description: documentNo
        ? `打开候选${applicationOnly ? '申请单' : '报销草稿'} ${documentNo} 核对详情。`
        : `打开候选${applicationOnly ? '申请单' : '报销草稿'}核对详情。`,
      icon: 'mdi mdi-open-in-new',
      action_type: 'open_application_detail',
      payload: {
        claim_id: applicationOnly ? candidate.applicationClaimId : candidate.claimId,
        claim_no: applicationOnly ? candidate.applicationClaimNo : candidate.claimNo,
        document_type: applicationOnly ? 'application' : 'expense',
        expense_case_id: candidate.expenseCaseId,
        application_claim_id: candidate.applicationClaimId,
        application_claim_no: candidate.applicationClaimNo
      }
    }
  })
}
