import { personaTagLabel } from '@pac/types';

/**
 * 把优先级三维翻成**这个患者的画像取值**。
 *
 * ── 一条铁律:只念画像,不从别处补 ──
 * 三维的输入本来就取自画像标签(急迫等级 / 价值分群 / 生命周期 / 潜在治疗,见 scorer 入参),
 * 所以这里就照抄画像的说法。**不要**为了"讲得更细"去 reason.signals、consultation_record
 * 之类的地方另外推一段话 —— 那是第二套口径,和「画像标签」抽屉一定会对不齐。
 *
 * 已经吃过两次亏:
 *   · 从 signals.triggers 反推出「种植 · 缺 6 颗」,而同屏的召回目标对 79 岁患者写的是
 *     「活动义齿为主」(生产 2,539 条这种);画像自己说的是「潜在种植」——
 *     三处三个说法。现在统一念画像那一个。
 *   · 把 intentBehavior=2 说成「没主动问过」,而它其实来自 consultation_record 且
 *     99.9% 是"查不到"。既然它不是画像标签,索性不出现在这里。
 *
 * 中文名一律走 personaTagLabel(圈人字典),不在本文件另抄映射表。
 */

export interface PriorityBreakdown {
  urgency?: number; // 急迫性 0-10
  value?: number; // 价值性 0-10
  willingness?: number; // 意愿度 0-10
  rfmAdherence?: number; // 意愿·RFM依从
  intentBehavior?: number; // 意愿·主诉行为
  trustBase?: number; // 意愿·信任基础
  freshness?: number; // 新鲜度因子 0.4-1
  confidenceFactor?: number; // 置信度因子(诊断1.0/建议0.8/影像0.5)
  base?: number; // 三维加权(未乘因子)
  raw?: number; // 综合 ×因子后
  /// 以下三项**仅供展示**,由 scorer 从画像原样带出(不参与计算)。老 plan 可能没有。
  rfmSegment?: string | null;
  lifecycleStage?: string | null;
  /// 画像·潜在治疗的标签原文,如 ['潜在种植','潜在修复']
  potentialLabels?: string[] | null;
}

/** reason.signals 里本模块用到的部分(其余字段不关心) */
export interface PriorityExplainSignals {
  triggers?: Array<{ code?: string | null }> | null;
  toothPosition?: string | null;
}

/// 急迫分值 → 画像·急迫等级的 code(scorer 的 URGENCY_SCORE 反向表,一一对应无歧义)
const URGENCY_CODE: Record<number, string> = { 10: 'urgent', 7: 'high', 4: 'mid', 1: 'low' };

export function explainUrgency(breakdown: PriorityBreakdown): string {
  const code = URGENCY_CODE[Math.round(breakdown.urgency ?? 0)];
  const zh = code ? personaTagLabel('urgency_level', code) : '';
  return zh ? `急迫等级 ${zh}` : '画像未标急迫等级';
}

/** 价值性 —— 念画像·潜在治疗。不从 reason.signals 反推治疗类型(见文件头) */
export function explainValue(breakdown: PriorityBreakdown): string {
  const labels = breakdown.potentialLabels ?? [];
  return labels.length ? `潜在治疗 ${labels.join(' / ')}` : '画像未标潜在治疗';
}

/** 意愿度 —— 念画像·价值分群 + 生命周期。主诉意向来自 consultation_record,不是画像,不出现在这里 */
export function explainWillingness(breakdown: PriorityBreakdown): string {
  const parts: string[] = [];
  const rfm = personaTagLabel('rfm', breakdown.rfmSegment);
  if (rfm) parts.push(`价值分群 ${rfm}`);
  const stage = personaTagLabel('lifecycle_stage', breakdown.lifecycleStage);
  if (stage) parts.push(`生命周期 ${stage}`);
  return parts.length ? parts.join(' · ') : '画像未标价值分群';
}

/** 新鲜度 —— 只在打了折时才值得占一行 */
export function explainFreshness(breakdown: PriorityBreakdown): string {
  const f = breakdown.freshness ?? 1;
  return f >= 1 ? '诊断还在黄金窗内' : '诊断偏老,已按时间降权';
}

/** 置信度 —— 因子直接对应信号来源 */
export function explainConfidence(breakdown: PriorityBreakdown): string {
  const c = breakdown.confidenceFactor ?? 1;
  if (c >= 1) return '来自医生诊断';
  if (c >= 0.8) return '来自医生建议';
  return '来自影像 AI,已降权';
}

/**
 * 池内位置的一句话。
 *
 * 「9.78 / 10」看着像"还行",实际是整池前 0.05%(生产池 p50 只有 3.1)——
 * 不给参照客服判断不了要不要先打他,这正是加这行的原因。
 */
export function explainPercentile(percentile?: number | null): string | null {
  if (percentile === null || percentile === undefined) return null;
  if (percentile >= 99) return '召回池里最该先打的一批(前 1%)';
  if (percentile >= 90) return `高于池内 ${percentile}% 的患者 · 优先打`;
  if (percentile >= 60) return `高于池内 ${percentile}% 的患者`;
  if (percentile >= 30) return `高于池内 ${percentile}% 的患者 · 中等`;
  return `高于池内 ${percentile}% 的患者 · 排在后面`;
}
