import {
  buildDraftRecallSummaryPrompt,
  DRAFT_RECALL_SUMMARY_SYSTEM,
  DRAFT_RECALL_SUMMARY_PROMPT_VERSION,
} from '../src/modules/ai/calls/draft-recall-summary/prompt';
import type { DraftRecallSummaryInput } from '../src/modules/ai/calls/draft-recall-summary/input.types';

/**
 * 历史联系摘要 —— 「未来排程 ≠ 漏做」回归。
 *
 * 生产事故(2026-07-24 排查):详情页「历史联系」把**诊所已排好、还没到日子**的回访
 * 说成"最新一次未执行,需优先补做"。实例:
 *   · 张学军 task_date=2026-10-09(77 天后)→ "10月9日最新一次洁牙复查回访未执行"
 *   · 江世琪 2026-09-17(55 天后)、史原 2026-08-25(32 天后)—— 同款措辞
 * 当时 934 条已生成摘要里 196 条"最新回访是未来排程",其中 158 条(81%)带
 * "未执行/补做/漏/尚未"措辞;对照组(最新在过去)只有 38%。
 *
 * 根因:prompt **没有今日锚点**,且按 taskDate desc 排序 → 未来排程恒排第一被当成"最新一次";
 *      taskStatus(已预约/已完成)也没喂给模型。
 *
 * 修复纪律(本文件锁住):日期比较是**确定性计算**,由 orchestrator 算好 isFuture 传入,
 * prompt 只负责把它渲染成显式标记 —— 跟话术侧"能程序算的事实全算好、LLM 只润色"同一条规矩。
 */

const TODAY = '2026-07-24';

function item(over: Partial<DraftRecallSummaryInput['items'][0]> = {}) {
  return {
    taskDate: '2026-04-24',
    type: '常规回访',
    status: '已回访',
    taskStatus: '已完成',
    isFuture: false,
    treatmentItems: null,
    followContent: null,
    result: '治疗后回访',
    ...over,
  };
}

function build(items: DraftRecallSummaryInput['items']) {
  return buildDraftRecallSummaryPrompt({ patientNameMasked: '张*', today: TODAY, items });
}

describe('buildDraftRecallSummaryPrompt — 时间锚 + 未来排程标记', () => {
  test('⭐ 必须把"今天"写进 prompt(模型据此才能判断远近)', () => {
    expect(build([item()])).toContain(`今天:${TODAY}`);
  });

  test('⭐ 未来排程条目带【未来排程·尚未到期】标记', () => {
    const p = build([
      item({ taskDate: '2026-10-09', status: '未回访', taskStatus: '未完成', isFuture: true, followContent: '复查洁牙' }),
    ]);
    expect(p).toContain('【未来排程·尚未到期】');
    expect(p).toContain('2026-10-09');
  });

  test('已过期条目**不带**未来标记(否则真漏做会被洗白)', () => {
    const p = build([item({ taskDate: '2026-03-02', status: '未回访', taskStatus: '未完成' })]);
    expect(p).not.toContain('【未来排程·尚未到期】');
  });

  test('⭐ 有未来排程时追加显式警告,并报出条数', () => {
    const p = build([
      item({ taskDate: '2026-10-09', isFuture: true }),
      item({ taskDate: '2026-09-17', isFuture: true }),
      item(),
    ]);
    expect(p).toContain('其中 2 条');
    expect(p).toMatch(/不得表述为未执行/);
  });

  test('全是历史条目 → 不出现未来排程警告(不给无谓噪声)', () => {
    const p = build([item(), item({ taskDate: '2026-03-02' })]);
    expect(p).not.toContain('尚未到期的未来排程');
  });

  test('⭐ taskStatus 必须进 prompt ——「已预约」不是「没做」', () => {
    const p = build([item({ taskStatus: '已预约', result: '复诊邀约' })]);
    expect(p).toContain('任务:已预约');
  });

  test('三位真实患者的原始形态:最新一条是未来排程 → 都被正确标注', () => {
    // 生产实测数据(2026-07-24 当天)
    const real: Array<[string, string]> = [
      ['2026-10-09', '复查洁牙'], // 张学军
      ['2026-09-17', '复查洁牙  看其他治疗是否要做，没有保险了'], // 江世琪
      ['2026-08-25', '46复查拍CT'], // 史原
    ];
    for (const [date, content] of real) {
      const p = build([
        item({ taskDate: date, status: '未回访', taskStatus: '未完成', isFuture: true, followContent: content }),
      ]);
      expect(p).toContain('【未来排程·尚未到期】');
    }
  });
});

describe('DRAFT_RECALL_SUMMARY_SYSTEM — 硬约束文本', () => {
  test('⭐ system 明令未来排程不得说成未执行 / 漏做 / 需补做', () => {
    for (const word of ['未执行', '漏做', '需补做', '未来排程']) {
      expect(DRAFT_RECALL_SUMMARY_SYSTEM).toContain(word);
    }
  });

  test('⭐ 明令"最近一次/最新一次"只能指已过期条目', () => {
    expect(DRAFT_RECALL_SUMMARY_SYSTEM).toMatch(/最近一次|最新一次/);
    expect(DRAFT_RECALL_SUMMARY_SYSTEM).toContain('已过期');
  });

  test('明令不要自己比日期(确定性计算已由程序完成)', () => {
    expect(DRAFT_RECALL_SUMMARY_SYSTEM).toContain('不要自己比日期');
  });

  test('promptVersion 已 bump(改了 prompt 必须换版本,否则缓存/评估串档)', () => {
    expect(DRAFT_RECALL_SUMMARY_PROMPT_VERSION).not.toBe('draft_recall_summary@2026-06-16-d');
    expect(DRAFT_RECALL_SUMMARY_PROMPT_VERSION).toMatch(/^draft_recall_summary@\d{4}-\d{2}-\d{2}-[a-z]$/);
  });
});
