<template>
  <article :class="['detail-card panel employee-risk-profile-card', `is-${decisionTone}`]">
    <div class="detail-card-head employee-risk-head">
      <div class="employee-risk-title-wrap">
        <h3 class="detail-card-title-with-icon">
          <i class="mdi mdi-file-document-alert-outline"></i>
          <span>{{ stageTitle }}</span>
        </h3>
        <span :class="['employee-risk-tone-pill', decisionTone]">{{ decisionBadgeLabel }}</span>
      </div>
    </div>

    <div class="employee-risk-body">
      <section :class="['employee-risk-decision-panel', decisionTone]">
        <div class="employee-risk-decision-main">
          <span>综合审核结论</span>
          <strong>{{ decisionTitle }}</strong>
          <p>{{ decisionDescription }}</p>
        </div>
        <div class="employee-risk-decision-action">
          <span>是否建议通过</span>
          <strong :class="decisionTone">{{ decisionBadgeLabel }}</strong>
          <p>{{ decisionAction }}</p>
        </div>
      </section>

      <dl class="employee-risk-review-summary" aria-label="审核建议摘要">
        <div
          v-for="item in reviewSummaryItems"
          :key="item.key"
          :class="['employee-risk-review-item', item.tone]"
        >
          <dt>{{ item.label }}</dt>
          <dd>{{ item.value }}</dd>
        </div>
      </dl>

      <section class="employee-risk-profile-section" aria-label="单据关键依据">
        <div class="employee-risk-section-head">
          <span>{{ stageBasisTitle }}</span>
          <small>{{ stageBasisHint }}</small>
        </div>
        <div v-if="compactEvidenceItems.length" class="employee-risk-profile-list">
          <details
            v-for="(item, index) in compactEvidenceItems"
            :key="item.code"
            :class="['employee-risk-evidence-row', item.tone]"
            :open="index === 0"
          >
            <summary class="employee-risk-evidence-title">
              <span>{{ item.label }}</span>
              <strong>{{ item.status }}</strong>
            </summary>
            <ul v-if="item.evidence.length">
              <li v-for="basis in item.evidence" :key="basis">{{ basis }}</li>
            </ul>
          </details>
        </div>
        <p v-else class="employee-risk-muted">当前未识别到需要重点展示的单据依据。</p>
      </section>
    </div>
  </article>
</template>

<script>
import { computed } from 'vue'

export default {
  name: 'StageRiskAdviceCard',
  props: {
    request: {
      type: Object,
      default: () => ({})
    },
    expenseItems: {
      type: Array,
      default: () => []
    },
    aiAdvice: {
      type: Object,
      default: () => ({})
    },
    isApplicationDocument: {
      type: Boolean,
      default: false
    }
  },
  setup(props) {
    const requestModel = computed(() => props.request || {})
    const currentItems = computed(() => Array.isArray(props.expenseItems) ? props.expenseItems : [])
    // 只消费语义层归一后的风险卡片，展示层不新增业务风险字段。
    const currentRiskCards = computed(() =>
      (Array.isArray(props.aiAdvice?.riskCards) ? props.aiAdvice.riskCards : [])
        .filter((card) => matchesCurrentStage(card, props.isApplicationDocument))
        .filter((card) => ['medium', 'high'].includes(normalizeTone(card?.tone)))
    )
    const highRiskCards = computed(() => currentRiskCards.value.filter((card) => normalizeTone(card?.tone) === 'high'))
    const mediumRiskCards = computed(() => currentRiskCards.value.filter((card) => normalizeTone(card?.tone) === 'medium'))
    const riskExplanationItems = computed(() => uniqueTexts(currentRiskCards.value.flatMap((card) => riskExplanationTexts([card]))))
    const materialIssues = computed(() => props.isApplicationDocument ? [] : resolveReimbursementMaterialIssues(currentItems.value))
    const sceneIssues = computed(() => resolveSceneIssues(requestModel.value, currentItems.value, props.isApplicationDocument))
    const decisionTone = computed(() => {
      if (highRiskCards.value.length) {
        return 'high'
      }
      if (mediumRiskCards.value.length || materialIssues.value.length || sceneIssues.value.length) {
        return 'medium'
      }
      return 'normal'
    })
    const stageTitle = computed(() => props.isApplicationDocument ? '申请审核建议' : 'AI建议')
    const stageBasisTitle = computed(() => props.isApplicationDocument ? '申请单关键依据' : '报销单关键依据')
    const stageBasisHint = computed(() => (
      props.isApplicationDocument
        ? '默认只展开最关键的申请依据，其他细节点开查看。'
        : '默认只展开最关键的报销依据，其他细节点开查看。'
    ))
    const decisionTitle = computed(() => resolveDecision(decisionTone.value, props.isApplicationDocument).title)
    const decisionAction = computed(() => {
      if (!props.isApplicationDocument && riskExplanationItems.value.length && ['medium', 'high'].includes(decisionTone.value)) {
        return '请核对已补充说明是否覆盖风险点，再决定通过或退回补充。'
      }
      return resolveDecision(decisionTone.value, props.isApplicationDocument).action
    })
    const decisionBadgeLabel = computed(() => {
      if (decisionTone.value === 'high') {
        return '不通过'
      }
      if (decisionTone.value === 'medium') {
        return '待补充'
      }
      return '可通过'
    })
    const decisionDescription = computed(() => {
      const riskCount = currentRiskCards.value.length
      const subject = props.isApplicationDocument ? '申请' : '报销'
      if (riskCount) {
        if (!props.isApplicationDocument && riskExplanationItems.value.length) {
          return `当前${subject}识别到 ${riskCount} 个需核对风险点，已补充说明但仍建议先核对票据与行程。`
        }
        return `当前${subject}识别到 ${riskCount} 个需核对风险点，请优先查看高风险依据。`
      }
      if (materialIssues.value.length || sceneIssues.value.length) {
        return `当前${subject}存在材料或业务说明不完整，建议补齐后再处理。`
      }
      return `当前${subject}未发现中高风险阻断项，可按流程继续处理。`
    })
    const stageEvidenceItems = computed(() => (
      props.isApplicationDocument ? buildApplicationEvidence() : buildReimbursementEvidence()
    ))
    const compactEvidenceItems = computed(() => {
      const abnormalItems = stageEvidenceItems.value.filter((item) => isAbnormalEvidence(item))
      const sourceItems = abnormalItems.length ? abnormalItems : stageEvidenceItems.value
      return sourceItems.map((item) => ({ ...item }))
    })
    const stageRiskFactSummary = computed(() => buildStageRiskFactSummary({
      isApplicationDocument: props.isApplicationDocument,
      riskCount: currentRiskCards.value.length,
      highCount: highRiskCards.value.length,
      mediumCount: mediumRiskCards.value.length,
      materialIssueCount: materialIssues.value.length,
      sceneIssueCount: sceneIssues.value.length
    }))
    const stageReviewBasisSummary = computed(() => buildStageReviewBasisSummary(
      compactEvidenceItems.value,
      props.isApplicationDocument
    ))
    const reviewSummaryItems = computed(() => [
      {
        key: 'fact',
        label: '风险概览',
        tone: decisionTone.value,
        value: stageRiskFactSummary.value
      },
      {
        key: 'basis',
        label: '重点依据',
        tone: decisionTone.value,
        value: stageReviewBasisSummary.value
      },
      {
        key: 'action',
        label: '审核建议',
        tone: decisionTone.value,
        value: decisionAction.value
      }
    ])

    function buildApplicationEvidence() {
      const budgetCards = currentRiskCards.value.filter((card) => /预算|余额|占用|超预算/.test(cardText(card)))
      const amountCards = currentRiskCards.value.filter((card) => /金额|标准|阈值|超标/.test(cardText(card)))
      return [
        evidenceItem('apply_amount', '申请金额与科目', amountCards.length ? '需复核' : '正常', amountCards.length ? highestTone(amountCards) : 'normal', [
          `申请科目：${displayValue(requestModel.value.typeLabel || requestModel.value.sceneLabel, '待确认')}`,
          `申请金额：${displayValue(requestModel.value.amountDisplay || requestModel.value.amount, '待确认')}`,
          ...riskTexts(amountCards)
        ]),
        evidenceItem('apply_budget', '预算触发规则', budgetCards.length ? '需复核' : '未命中', budgetCards.length ? highestTone(budgetCards) : 'normal', (
          budgetCards.length ? riskTexts(budgetCards) : ['当前申请暂未命中预算余额或预算占用类中高风险。']
        )),
        evidenceItem('apply_scene', '申请事由与场景', sceneIssues.value.length ? '待补充' : '已说明', sceneIssues.value.length ? 'medium' : 'normal', [
          `申请事由：${displayValue(requestModel.value.reason, '待补充')}`,
          `申请目的地/发生地点：${displayValue(requestModel.value.location || requestModel.value.sceneTarget, '待补充')}`,
          `申请时间：${displayValue(requestModel.value.period || requestModel.value.occurredDisplay, '待补充')}`,
          ...sceneIssues.value.map((item) => `当前缺少：${item}`)
        ]),
        evidenceItem('apply_risk', '申请规则命中', currentRiskCards.value.length ? '有风险' : '无异常', decisionTone.value, (
          currentRiskCards.value.length ? riskTexts(currentRiskCards.value) : ['申请环节未命中中高风险规则。']
        ))
      ]
    }

    function buildReimbursementEvidence() {
      const riskGroups = classifyReimbursementRiskCards(currentRiskCards.value)
      const attachmentCards = riskGroups.attachment
      const amountCards = riskGroups.amount
      const routeCards = riskGroups.route
      const otherCards = riskGroups.other
      const needAttachmentItems = currentItems.value.filter((item) => !item?.isSystemGenerated)
      const uploadedCount = needAttachmentItems.filter((item) => String(item?.invoiceId || '').trim()).length
      const evidenceItems = [
        evidenceItem('reimburse_attachment', '票据与附件', materialIssues.value.length || attachmentCards.length ? '需核对' : '完整', materialIssues.value.length || attachmentCards.length ? highestTone(attachmentCards, 'medium') : 'normal', [
          `需附件明细 ${needAttachmentItems.length} 条，已关联 ${uploadedCount} 条，未上传 ${materialIssues.value.length} 条。`,
          ...materialIssues.value.slice(0, 3),
          ...riskTexts(attachmentCards)
        ]),
        evidenceItem('reimburse_amount', '报销金额与明细', amountCards.length ? '需复核' : '正常', amountCards.length ? highestTone(amountCards) : 'normal', [
          ...riskTexts(amountCards),
          ...riskExplanationTexts(amountCards),
          `报销金额：${displayValue(requestModel.value.amountDisplay || requestModel.value.amount, '待确认')}`,
          `费用明细：${currentItems.value.length} 条，明细合计 ${formatCurrency(totalItemAmount(currentItems.value))}。`
        ]),
        evidenceItem('reimburse_route', '行程/时间/地点', routeCards.length || sceneIssues.value.length ? '需核对' : '已匹配', routeCards.length ? highestTone(routeCards) : sceneIssues.value.length ? 'medium' : 'normal', [
          ...riskTexts(routeCards),
          ...riskExplanationTexts(routeCards),
          `报销事由：${displayValue(requestModel.value.reason, '待补充')}`,
          `报销地点/目的地：${displayValue(requestModel.value.location || requestModel.value.sceneTarget, '待补充')}`,
          ...sceneIssues.value.map((item) => `当前缺少：${item}`)
        ])
      ]
      if (otherCards.length || (!amountCards.length && !routeCards.length && !attachmentCards.length)) {
        evidenceItems.push(evidenceItem('reimburse_risk', '其他规则命中', otherCards.length ? '有风险' : '无异常', otherCards.length ? highestTone(otherCards) : decisionTone.value, (
          otherCards.length ? riskTexts(otherCards) : ['报销环节未命中中高风险规则。']
        )))
      }
      return evidenceItems
    }

    function evidenceItem(code, label, status, tone, evidence) {
      return {
        code,
        label,
        status,
        tone,
        evidence: uniqueTexts(evidence).filter(Boolean).slice(0, 5)
      }
    }

    return {
      compactEvidenceItems,
      decisionBadgeLabel,
      decisionTone,
      decisionDescription,
      decisionAction,
      decisionTitle,
      reviewSummaryItems,
      stageBasisHint,
      stageBasisTitle,
      stageEvidenceItems,
      stageReviewBasisSummary,
      stageRiskFactSummary,
      stageTitle
    }
  }
}

function buildStageRiskFactSummary({
  isApplicationDocument,
  riskCount = 0,
  highCount = 0,
  mediumCount = 0,
  materialIssueCount = 0,
  sceneIssueCount = 0
} = {}) {
  const subject = isApplicationDocument ? '申请单' : '报销单'
  if (riskCount > 0) {
    return `${subject}识别 ${riskCount} 个需核对风险点，高风险 ${highCount} 个，中风险 ${mediumCount} 个。`
  }
  const issueCount = materialIssueCount + sceneIssueCount
  if (issueCount > 0) {
    return `${subject}暂无中高风险命中，但仍有 ${issueCount} 个材料或业务说明项需要补齐。`
  }
  return `${subject}未识别到中高风险阻断项。`
}

function buildStageReviewBasisSummary(evidenceItems = [], isApplicationDocument = false) {
  const abnormalLabels = evidenceItems
    .filter((item) => isAbnormalEvidence(item))
    .map((item) => String(item?.label || '').trim())
    .filter(Boolean)
  if (abnormalLabels.length) {
    return `重点核对${abnormalLabels.join('、')}。`
  }
  return isApplicationDocument
    ? '重点看申请金额、预算触发和事由是否一致。'
    : '重点看票据、金额、行程和附件是否一致。'
}

function resolveDecision(tone, isApplicationDocument) {
  const map = {
    normal: {
      title: '建议通过',
      action: isApplicationDocument
        ? '可按权限继续审核，系统会按预算结果决定是否进入下一步。'
        : '可按权限继续审批，后续进入财务或付款流程。'
    },
    medium: {
      title: '建议补充后通过',
      action: isApplicationDocument
        ? '建议补充预算占用、申请事由和金额依据后再通过。'
        : '建议补充票据、金额或业务说明后再通过。'
    },
    high: {
      title: '不建议通过',
      action: isApplicationDocument
        ? '建议退回补充申请依据，或要求预算管理者复核。'
        : '建议退回补充票据、行程说明或超标原因。'
    }
  }
  return map[tone] || map.normal
}

function isAbnormalEvidence(item) {
  const tone = normalizeTone(item?.tone)
  const status = String(item?.status || '').trim()
  if (tone === 'medium' || tone === 'high') {
    return true
  }
  return !['正常', '未命中', '已说明', '完整', '已匹配', '无异常'].includes(status)
}

function classifyReimbursementRiskCards(cards = []) {
  const groups = {
    attachment: [],
    amount: [],
    route: [],
    other: []
  }
  for (const card of cards) {
    const text = cardText(card)
    if (isAmountRiskText(text)) {
      groups.amount.push(card)
    } else if (isRouteRiskText(text)) {
      groups.route.push(card)
    } else if (isAttachmentRiskText(text)) {
      groups.attachment.push(card)
    } else {
      groups.other.push(card)
    }
  }
  return groups
}

function isAmountRiskText(value) {
  return /金额|标准|阈值|超标|超出|报销标准|住宿标准|差标|费用上限/.test(String(value || ''))
}

function isRouteRiskText(value) {
  return /多城市|中转|多地|改签|城市|行程|交通|出差|地点|日期|时间|目的地|起始地|返回|火车|高铁|机票|航班/.test(String(value || ''))
}

function isAttachmentRiskText(value) {
  return /附件|票据|发票|OCR|识别|单据/.test(String(value || ''))
}

function matchesCurrentStage(card, isApplicationDocument) {
  const businessStage = resolveCardBusinessStage(card)
  if (businessStage) {
    return isApplicationDocument
      ? businessStage === 'expense_application'
      : businessStage === 'reimbursement'
  }

  const text = cardText(card)
  if (isApplicationDocument) {
    return !/报销|附件|单据|发票|票据|OCR|识别|付款|支付/.test(text) || /申请|预算|额度|事前|预估|审批/.test(text)
  }
  return !/申请环节|事前申请|预算申请/.test(text)
}

function resolveCardBusinessStage(card = {}) {
  const candidates = [
    card.businessStage,
    card.business_stage,
    card.controlStage,
    card.control_stage
  ]
  for (const candidate of candidates) {
    const stage = normalizeBusinessStage(candidate)
    if (stage) {
      return stage
    }
  }
  return ''
}

function normalizeBusinessStage(value) {
  const stage = String(value || '').trim().toLowerCase()
  if ([
    'expense_application',
    'application',
    'apply',
    'pre_apply',
    'pre_application',
    'budget_application'
  ].includes(stage)) {
    return 'expense_application'
  }
  if ([
    'reimbursement',
    'expense_reimbursement',
    'claim',
    'expense_claim',
    'expense_report'
  ].includes(stage)) {
    return 'reimbursement'
  }
  return ''
}

function resolveReimbursementMaterialIssues(items) {
  return items
    .filter((item) => !item?.isSystemGenerated && isRequiredMaterialItem(item) && !String(item?.invoiceId || '').trim())
    .map((item) => `住宿材料待补充：${item.name || item.category || item.desc || '住宿明细'}`)
}

function isRequiredMaterialItem(item) {
  const text = [
    item?.itemType,
    item?.typeCode,
    item?.name,
    item?.category,
    item?.desc,
    item?.itemReason
  ].map((value) => String(value || '').trim()).join(' ')
  return /hotel_ticket|hotel|住宿|酒店|水单/.test(text)
}

function resolveSceneIssues(request, items, isApplicationDocument) {
  const missing = []
  if (isMissing(request.reason)) {
    missing.push(isApplicationDocument ? '申请事由' : '报销事由')
  }
  if (isMissing(request.location) || isMissing(request.sceneTarget)) {
    missing.push(isApplicationDocument ? '申请地点/目的地' : '报销地点/目的地')
  }
  if (isMissing(request.period) || isMissing(request.occurredDisplay)) {
    missing.push(isApplicationDocument ? '申请发生时间' : '报销发生时间')
  }
  if (!isApplicationDocument) {
    const itemMissingReasonCount = items.filter((item) => isMissing(item?.itemReason || item?.desc)).length
    if (itemMissingReasonCount) {
      missing.push(`${itemMissingReasonCount} 条报销明细缺少事由`)
    }
  }
  return missing
}

function normalizeTone(value) {
  const tone = String(value || '').trim().toLowerCase()
  if (tone === 'high') return 'high'
  if (tone === 'medium') return 'medium'
  return 'normal'
}

function highestTone(cards, fallback = 'normal') {
  if (!cards.length) return fallback
  if (cards.some((card) => normalizeTone(card?.tone) === 'high')) return 'high'
  if (cards.some((card) => normalizeTone(card?.tone) === 'medium')) return 'medium'
  return fallback
}

function riskTexts(cards) {
  return cards
    .map((card) => stripEmbeddedExplanationText(card?.risk || card?.summary || card?.title || ''))
    .filter(Boolean)
    .slice(0, 4)
}

function riskExplanationTexts(cards) {
  return uniqueTexts(cards.flatMap((card) => {
    const summary = String(card?.relatedExplanationSummary || '').trim()
    const entries = Array.isArray(card?.relatedExplanations)
      ? card.relatedExplanations.map((item) => String(item?.text || item?.note || '').trim())
      : []
    if (summary) {
      return [`已补充异常说明：${summary}`]
    }
    return entries.map((item) => item ? `已补充异常说明：${item}` : '')
  })).slice(0, 4)
}

function stripEmbeddedExplanationText(value) {
  return String(value || '')
    .trim()
    .replace(/，?用户已在相关费用明细补充异常说明[^。；;]*[。；;]?/g, '')
    .replace(/，?用户已在费用明细补充异常说明[^。；;]*[。；;]?/g, '')
    .replace(/[，,；;。]+$/g, '')
    .trim()
}

function cardText(card) {
  return [
    card?.label,
    card?.title,
    card?.risk,
    card?.summary,
    card?.suggestion,
    ...(Array.isArray(card?.ruleBasis) ? card.ruleBasis : [])
  ]
    .map((item) => String(item || '').trim())
    .join(' ')
}

function displayValue(value, fallback) {
  const text = String(value || '').trim()
  return isMissing(text) ? fallback : text
}

function isMissing(value) {
  const text = String(value || '').trim()
  return !text || ['待补充', '暂无', '无', 'null', 'undefined'].includes(text)
}

function totalItemAmount(items) {
  return items.reduce((sum, item) => sum + safeNumber(item?.itemAmount), 0)
}

function safeNumber(value) {
  const amount = Number(value)
  return Number.isFinite(amount) ? amount : 0
}

function formatCurrency(value) {
  return `${safeNumber(value).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} 元`
}

function uniqueTexts(values) {
  return [...new Set(values.map((item) => String(item || '').trim()).filter(Boolean))]
}
</script>

<style scoped src="../../assets/styles/components/stage-risk-advice-card.css"></style>
