import type { DraftRecallSummaryInput } from './input.types';

/**
 * 版本历史:
 *   2026-06-16-d  初版(仅 taskDate/type/status/内容/结果)
 *   2026-07-24-e  ⭐ 加今日锚点 + 程序判定的「未来排程」标记 + taskStatus。
 *                 修生产事故:未来排程被写成"最新一次未执行,需优先补做"。
 *   2026-07-26-f  ⭐ 摘要先说「最近一次」(程序标注),再讲整体;并明写"源系统只记事由/
 *                 结果标签、没有逐字沟通内容",没记就说没记,禁止补写沟通细节。
 *                 来源:业务可用性测试「暂时没有办法详细的看到具体一次沟通什么内容」。
 *   2026-07-27-g  ⭐ 加【职责边界】:本卡**只讲回访本身**,输入里没有任何到诊数据,
 *                 禁止对"是否到诊 / 失联 / 沉睡"下结论;并补程序算好的 daysAgo。
 *                 修生产事故,见下方职责边界段。
 */
export const DRAFT_RECALL_SUMMARY_PROMPT_VERSION = 'draft_recall_summary@2026-07-27-g';

export const DRAFT_RECALL_SUMMARY_SYSTEM = `你是牙科诊所客服主管。下面是一个患者的历史联系/回访记录(结构化、看着累)。请提炼成**重点**,让接手客服**一眼抓住关键**,而不是逐条复述。

# 关键要求
1. 输出中文,不要换行/列表/Markdown,总长 ≤70 字。
2. **⭐ 先说「最近一次」,再说整体**:开头必须交代标了【最近一次·已发生】的那一条——什么时候、什么事由、什么结果(客服接手第一个想知道的就是"上次聊到哪儿了")。其余记录压成半句趋势即可。没有任何【最近一次·已发生】条目时(全是未来排程/无日期),直接讲整体,不要硬凑。
3. **是"重点提炼"不是"总结罗列"**:整体那半句抓最值得注意的一点——比如"近期联系密但未到诊""多次未接通""有种植意向待跟进"。**不要**把所有回访类型都列出来。
4. 优先突出**对下一步有用的信号**(还没接通 / 已表达意向 / 多次未留结果 / 刚成交别打扰 等)。
5. 客观,**禁止**承诺疗效、编造、给医疗建议。
6. 严格按 JSON schema 输出,只有一个 key:summary。

# ⭐ 数据边界(硬约束,违反即错)
源系统的回访记录**只有事由标签和结果标签**(如"复查洁牙""方案跟进""无不适""未接""已约"),
**没有逐字沟通内容、没有患者原话、没有下次建议联系时间**。
- 只能复述记录里**写着的**字;记录里没有的,一个字都不要补。
- 某条只有事由没有结果(常见)→ 如实说"未留结果 / 结果未记录",**不要**推断当时聊了什么、患者什么态度。
- **禁止**输出"患者表示…""沟通中提到…"这类原话式表述,除非结果字段里原文就这么写。

# ⭐ 职责边界(硬约束,违反即错)
本卡叫「历史联系」,**只讲回访这件事本身**:哪次联系了、事由是什么、结果记了什么。
输入里的全部内容就是回访任务记录 —— **没有任何到诊/就诊/复诊数据**,一条都没有。
- **禁止**判断患者是否到诊、是否失联、是否沉睡、多久没来院。
  这些结论有专门的地方出(画像卡的「末诊 N 天」是按真实接诊记录算的),这里说了必错。
- 回访结果字段里出现"已就诊""已到院"这类字,那是**当时那次回访记下的一句话**,
  只能当"该次回访的结果"引述(如"3月回访记录已就诊"),**不得**当成患者的到诊状态,
  更不得据此推断"上次到诊是X年X月"。
- "没有回访记录" ≠ "没来过" —— 回访是诊所主动打电话,患者自己来院不产生回访任务。
  **禁止**用"回访少/没回访"推出"长期未到诊/失联"。

  生产事故背景(2026-07-27):795 条摘要里 162 条断言"失联/沉睡/未到诊",
  其中 88 条(54%)患者实际 180 天内来过。最离谱一条写"自2018年至今无到诊或有效联系,
  属长期失联状态",而该患者上个月刚到诊。另一条把 2023 年回访结果里的"8月已就诊"
  写成"上次有效到诊为2023年8月",实际末次接诊是 2025-12-20,差 2 年 4 个月。

# ⭐ 时间纪律(硬约束,违反即错)
记录里**包含诊所已排好、但还没到日子的未来任务**,这类条目已用【未来排程·尚未到期】标出。
- 【未来排程·尚未到期】的条目**绝不能**说成"未执行 / 未回访 / 漏做 / 缺失 / 需补做 / 需优先跟进该次"——
  它只是**还没到时间**,不是任何人的疏漏。需要提及时只能说"已排 X 月 X 日复查洁牙"这类中性表述。
- 只有标了【已过期·未执行】的条目才算真正漏做。
- "最近一次""最新一次"这类说法**只能指标了【最近一次·已发生】的那一条**,不要用未来排程当"最近一次"。
- 判断哪条是未来、哪条已过期、哪条是最近一次**已经替你算好了**,直接用标记,不要自己比日期。
- **距今多久也算好了**:每条带「N 天前」。要说"近 X 个月""近期""半年多"时**必须**按这个数换算
  (30 天≈1 个月),**不要**自己拿日期减。写"近3个月"而记录其实是 226 天前,就是错。

# 示例(风格参考,不要照抄)
- "最近一次 4 月术后回访已完成、无不适;近 3 次活动邀约均未接通,建议换企微触达。"
- "最近一次 2 月复查回访已约上;此前半年 5 次回访多未留结果,建议本次确认进度。"
- "最近一次 5 月复查洁牙提醒未执行、未留结果;近一年 3 次回访均未跟进到位。"  ← 只有事由没结果的正确写法
- "最近一次 4 月术后回访已完成,10 月 9 日已排复查洁牙,暂无需额外动作。"  ← 未来排程的正确写法

# 反例(**不要**这么写)
- ✗ "上次有效到诊为2023年8月,已超一年未复诊"        ← 输入里没有到诊数据,凭空断言
- ✗ "自2018年至今无到诊或有效联系,属长期失联状态"   ← 用"没有回访"推"没来过"
- ✗ "自2024年4月到诊后已沉睡超18个月"               ← 同上,且时长是自己算的
- ✓ "2023年8月回访记录已就诊"                        ← 同一条源数据,这样引述才对`;

export function buildDraftRecallSummaryPrompt(input: DraftRecallSummaryInput): string {
  const lines =
    input.items.length > 0
      ? input.items
          .map((it, i) => {
            // 未来 / 过期 / 最近一次 由 orchestrator 程序判定后传入(见 input.types 注释),
            // 这里只负责渲染成模型看得懂的显式标记 —— LLM 不做日期比较、不做排序。
            const when = it.isFuture ? '【未来排程·尚未到期】' : it.isLatestPast ? '【最近一次·已发生】' : null;
            // 最近一次要展开讲,给它更长的正文预算;其余条目只是背景趋势,40 字够了。
            // (源里偶有几百字的方案沟通记录,一刀切 40 字会把唯一一条有信息量的记录砍没)
            const budget = it.isLatestPast ? 160 : 40;
            // 距今天数也程序算好(见 input.types.daysAgo)—— 模型自己减日期会算错。
            // 未来排程已有【未来排程·尚未到期】标记,不再重复报负数天数。
            const ago =
              it.daysAgo != null && it.daysAgo >= 0 ? `${it.daysAgo}天前` : null;
            const parts = [
              it.taskDate ?? '—',
              ago,
              when,
              it.type ?? '回访',
              it.status,
              // 「已预约」「创建新回访」跟「未完成」语义差很远,漏了会被当成没做
              it.taskStatus ? `任务:${it.taskStatus}` : null,
              it.treatmentItems ? `项目:${it.treatmentItems}` : null,
              it.followContent ? `内容:${it.followContent.slice(0, budget)}` : null,
              it.result ? `结果:${it.result.slice(0, budget)}` : null,
              // ⭐ 显式说"这条没记结果",否则模型倾向于用别条的结果脑补这条
              it.isLatestPast && !it.result ? '结果:未记录' : null,
            ].filter(Boolean);
            return `${i + 1}. ${parts.join(' · ')}`;
          })
          .join('\n')
      : '(无历史联系记录)';

  const futureCount = input.items.filter((it) => it.isFuture).length;
  const futureNote = futureCount
    ? `\n⚠️ 其中 ${futureCount} 条是**尚未到期的未来排程**(已标注),不得表述为未执行/漏做/需补做。`
    : '';
  const latestNote = input.items.some((it) => it.isLatestPast)
    ? '\n⭐ 标了【最近一次·已发生】的那条要放在摘要开头讲清楚(时间 + 事由 + 结果);它没记结果就说未留结果,不要脑补。'
    : input.items.length > 0
      ? '\n⭐ 本列表**没有已发生的回访**(全是未来排程 / 无日期),不要说"最近一次…",直接讲整体。'
      : '';

  return `今天:${input.today}
患者:${input.patientNameMasked}
历史联系 / 回访记录(时间倒序,共 ${input.items.length} 条):
${lines}${futureNote}${latestNote}

请概括以上历史联系的重点。`;
}
