import {
  explainConfidence,
  explainPercentile,
  explainUrgency,
  explainValue,
  explainWillingness,
  type PriorityBreakdown,
} from '../../pac-web/src/components/priority-explain';

/**
 * 优先级 hover 的逐患者文案回归。
 *
 * ⚠️ 测试放在 pac-service 是因为 pac-web 没有测试环境,而这个模块是**纯函数、零 React 依赖**
 * (PriorityBreakdown 类型也因此从 priority-hover.tsx 挪了出来)。
 *
 * ── 为什么值得测 ──
 * 这些字是客服照着跟患者开口用的,而且必须和「画像标签」抽屉说同一句话。踩过两次:
 *
 *   ① 从 reason.signals 反推出「种植 · 缺 6 颗」。可同一屏上,79 岁患者的召回目标写的是
 *      「活动义齿为主」,画像自己写的是「潜在种植」—— 三处三个说法(生产 2,539 条这种)。
 *   ② 把 intentBehavior=2 说成「没主动问过」。它来自 consultation_record 不是画像,
 *      而且生产 22 万个 2 分患者里真的没咨询过的只有 230 人(0.1%)—— 80% 是源端
 *      intent/content 全为 null 查不出来,20% 是问了别的项目。这话 99.9% 是错的。
 *
 * 结论就是本文件第一组断言:**只念画像标签的取值,别处一律不补**。
 */

const bd = (over: Partial<PriorityBreakdown> = {}): PriorityBreakdown => ({
  urgency: 10,
  value: 10,
  willingness: 2.63,
  freshness: 1,
  confidenceFactor: 1,
  rfmSegment: 'important_winback',
  lifecycleStage: 'churned',
  potentialLabels: ['潜在种植'],
  ...over,
});

describe('⭐ 红线:只念画像,不从别处补第二套口径', () => {
  test('三维文案里不出现非画像来源的说法', () => {
    const text = [
      explainUrgency(bd()),
      explainValue(bd({ potentialLabels: ['潜在种植'] })),
      explainWillingness(bd()),
    ].join(' ');
    // 这些都曾被从 reason.signals / consultation_record 推出来过,和画像抽屉对不齐:
    //   「缺 N 颗」「种植」来自 signals;「没主动问过」来自 consultation_record
    for (const forbidden of ['缺 ', '没主动问过', '没问过', '未咨询', '被动', '咨询']) {
      expect(text).not.toContain(forbidden);
    }
  });

  test('价值性念画像·潜在治疗的原文', () => {
    expect(explainValue(bd({ potentialLabels: ['潜在种植', '潜在修复'] }))).toBe(
      '潜在治疗 潜在种植 / 潜在修复',
    );
  });

  test('画像没标就说没标,不编', () => {
    expect(explainValue(bd({ potentialLabels: [] }))).toBe('画像未标潜在治疗');
    expect(explainUrgency(bd({ urgency: 0 }))).toBe('画像未标急迫等级');
    expect(explainWillingness(bd({ rfmSegment: null, lifecycleStage: null }))).toBe('画像未标价值分群');
  });
});

describe('三维念的是画像标签取值', () => {
  test('急迫性 = 画像·急迫等级(中文取自圈人字典)', () => {
    expect(explainUrgency(bd({ urgency: 10 }))).toBe('急迫等级 紧急');
    expect(explainUrgency(bd({ urgency: 7 }))).toBe('急迫等级 高');
    expect(explainUrgency(bd({ urgency: 4 }))).toBe('急迫等级 中');
  });

  test('意愿度 = 画像·价值分群 + 生命周期', () => {
    expect(explainWillingness(bd({ rfmSegment: 'important_winback', lifecycleStage: 'churned' }))).toBe(
      '价值分群 重要挽留 · 生命周期 流失客',
    );
  });

  test('老 plan 缺字段时不出 undefined', () => {
    for (const t of [explainUrgency(bd({ urgency: undefined })), explainValue(bd({ potentialLabels: null })),
                     explainWillingness(bd({ rfmSegment: null, lifecycleStage: null }))]) {
      expect(t).not.toContain('undefined');
      expect(t.length).toBeGreaterThan(0);
    }
  });

  test('置信度对应信号来源(这一维本就不是画像,是信号来源)', () => {
    expect(explainConfidence(bd({ confidenceFactor: 1 }))).toBe('来自医生诊断');
    expect(explainConfidence(bd({ confidenceFactor: 0.8 }))).toBe('来自医生建议');
    expect(explainConfidence(bd({ confidenceFactor: 0.5 }))).toContain('影像');
  });
});

describe('池内位置 —— 回答"这分算高还是低"', () => {
  test('缺失时不渲染(池太小 / 老响应)', () => {
    expect(explainPercentile(null)).toBeNull();
    expect(explainPercentile(undefined)).toBeNull();
  });

  test('⭐ 100 分位不能说成「高于池内 100% 的患者」(他自己也在池里)', () => {
    expect(explainPercentile(100)).not.toContain('100%');
    expect(explainPercentile(100)).toContain('前 1%');
  });

  test('各档都给得出话', () => {
    for (const p of [0, 15, 45, 75, 92, 99]) {
      expect(explainPercentile(p)?.length ?? 0).toBeGreaterThan(0);
    }
  });
});
