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 挪了出来)。
 *
 * ── 为什么值得测 ──
 * 这些字是客服照着跟患者开口用的,写错比不写更糟。已经踩过一次:
 * intentBehavior=2 曾被写成「没主动问过」,而生产实测 22 万个 2 分患者里
 *   真的从没咨询过的只有 230 人(0.1%)
 *   咨询过但源端 intent/content 全为 null,解析不出意向的 176,578 人(80%)
 *   咨询过、有意向但问的是别的项目 43,911 人(20%)
 * ——「没主动问过」在 99.9% 的情况下是错的,客服拿去讲会当场翻车。
 */

const bd = (over: Partial<PriorityBreakdown> = {}): PriorityBreakdown => ({
  urgency: 10,
  value: 10,
  willingness: 2.63,
  intentBehavior: 2,
  freshness: 1,
  confidenceFactor: 1,
  rfmSegment: 'important_winback',
  lifecycleStage: 'churned',
  ...over,
});

describe('⭐ 红线:数据缺失不能说成"患者没做过"', () => {
  test('查不到咨询意向 → 只陈述查不到,不替患者下结论', () => {
    const text = explainWillingness(bd({ intentBehavior: 2 }));
    expect(text).toContain('没查到咨询意向');
    // 这些措辞都是在断言患者的行为,而我们只知道"记录里没有"
    for (const forbidden of ['没主动问过', '没问过', '没咨询', '未咨询', '被动']) {
      expect(text).not.toContain(forbidden);
    }
  });

  test('查到了才说问过 —— 8 分要求意向命中本次治疗类别,是确凿的', () => {
    expect(explainWillingness(bd({ intentBehavior: 8 }))).toContain('问过这个项目');
  });
});

describe('三维文案讲的是这个患者', () => {
  test('急迫性按档位翻译', () => {
    expect(explainUrgency(bd({ urgency: 10 }))).toBe('紧急 · 90 天以上没来');
    expect(explainUrgency(bd({ urgency: 7 }))).toBe('高 · 30-90 天没来');
    expect(explainUrgency(bd({ urgency: 0 }))).toBe('无待做项目');
  });

  test('⭐ 价值性带牙数分档(种植的分值就是按牙数抬的,只报名字等于没说)', () => {
    const s = (teeth: string) => ({ triggers: [{ code: 'K08' }], toothPosition: teeth });
    expect(explainValue(bd(), s('16'))).toBe('种植 · 缺 1 颗');
    expect(explainValue(bd(), s('16;17;27'))).toBe('种植 · 缺 3 颗');
    expect(explainValue(bd(), s('11;12;13;14;15;16;17;21'))).toBe('种植 · 缺 6 颗以上');
  });

  test('signals 缺失时退回分档数字,不编造治疗类型', () => {
    expect(explainValue(bd({ value: 7 }), null)).toBe('预估收入档 7/10');
  });

  test('意愿度 = RFM 分群 · 咨询意向 · 生命周期(中文名取自圈人字典)', () => {
    expect(explainWillingness(bd({ rfmSegment: 'important_winback', lifecycleStage: 'churned' }))).toBe(
      '重要挽留 · 没查到咨询意向 · 流失客',
    );
  });

  test('老 plan 没有 rfmSegment / lifecycleStage → 降级但不出 undefined', () => {
    const text = explainWillingness(bd({ rfmSegment: null, lifecycleStage: null }));
    expect(text).not.toContain('undefined');
    expect(text.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);
    }
  });
});
