/**
 * DraftRecallBrief AiCall — 把"本次召回原因 + 历史治疗 + 画像"提炼成**一句话召回简报**。
 *
 * 用途:详情页「参考话术」标题下方那行(原 RecallReasonLine 结构化富文本),压成一句话,
 * 回答客服打这通电话最该心里有数的四问:
 *   ① 患者是谁(从生命周期 / 价值 / 类型看)
 *   ② 帮患者解决什么问题(应治未治的缺口)
 *   ③ 邀约到诊是做什么(具体治疗动作)
 *   ④ 以什么切入(复查建议)—— 低门槛开口台阶,必须锚在 reviewAnchors 事实上,无事实不得编
 *
 * 跟 DraftRecallSummary / DraftPersonaSummary 平行,但输入更全(三类信号合一)。
 */
export interface DraftRecallBriefInput {
  patientNameMasked: string;
  /** 画像标签(含生命周期 / 价值分群 / 人群细分 → 判断"患者是谁")*/
  persona: Array<{ label: string; value: string }>;
  /** 历史治疗类目 + 次数(已做过什么,判断熟客/新客 + 信任锚)*/
  treatmentHistory: Array<{ category: string; count: number }>;
  /** 本次召回原因(应治未治项)*/
  reasons: Array<{
    subLabel: string; // 子场景中文(如 缺失牙未启动修复;仅医生建议时为建议名"建议种植")
    /** 触发来源:diagnosis=医生确诊 / recommendation=医生建议(非确诊) / image_finding=影像AI。
     *  recommendation 时**不可假设牙已缺失/空着**,按医生建议的治疗动作措辞。 */
    source: 'diagnosis' | 'recommendation' | 'image_finding';
    diagnosis: string | null; // 触发诊断中文(如 牙列丢失/缺牙);仅建议时为 null
    tooth: string | null; // 牙位(如 47)
    daysSinceText: string; // 距今(如 131 天(4 个月))
    expectedCategories: string[]; // 期望/未启动治疗类目中文(如 种植 / 修复 / 冠桥)
  }>;
  /**
   * 复查切入锚点(第④问的兜底事实依据,防编造):
   *   lastVisitText    — 最近一次实际治疗距今(如 8 个月前);null=无记录
   *   lastCleaningText — 最近一次洁牙/检查/牙周洁治距今;null=无记录
   * 仅当 doctorGuidance 为空时使用;LLM 只能引用这里的值,没给就用不带时间的通用复查邀约。
   */
  reviewAnchors: {
    lastVisitText: string | null;
    lastCleaningText: string | null;
  };
  /**
   * 医生计划/建议/医嘱(第④问的**最优先**事实依据)—— 统一条目形态,核心是 verbatim 原话属性:
   *   kind     — plan=治疗计划(treatment_record kind=planned) / recommendation=医生建议 / advice=医嘱(emr.doctor_advice)
   *   label    — 归一名(治疗类目 / 建议名;医嘱固定 '医嘱')
   *   verbatim — **原话**(计划=subtype 原文 / 建议=name 原文 / 医嘱=doctor_advice 原文;截断 ≤40 字);null=只有大类
   * 有任一条 → 切入围绕医生真实交代展开(优先引用原话要点),不再引用洁牙锚点时间。
   */
  doctorGuidance: Array<{
    kind: 'plan' | 'recommendation' | 'advice';
    label: string;
    verbatim: string | null;
    tooth: string | null;
    atText: string | null;
  }>;
  /**
   * 历史回访联系(第②点的事实依据)—— 最近几条,新到旧。
   * ⚠️ 只给**已发生**的联系记录,⛔ 别把未来的预约回访塞进来:
   *   第②点要回答"以前怎么联系的、结果如何",把还没发生的说成历史就是编。
   * 空数组 = 从没联系过(新客),第②点如实说"此前无回访记录",⛔ 不许留白也不许编。
   */
  returnVisitHistory: Array<{
    /// 距今(如 2 个月前)。⚠️ 相邻两条可能落到同一个粗粒度描述上,故 outcomeText 必须能区分
    atText: string;
    type: string | null; // 回访类型(中文)
    /** **这次回访是干什么的**(follow_content):半年洁牙提醒 / 种植潜客 / 术后复查… */
    topicText: string | null;
    /**
     * **结果怎么样**(result):未接 / 诊后回访无不适 / 约时间 / 复查…
     *
     * 🔴 这个字段一开始**漏喂了**(2026-08-06 实测):没有主题的那条只能输出「没留下结果」,
     *   而库里写着「诊后回访未接」,详情页「历史联系」卡据此说了「未接通」——
     *   两处一对比,简报既含糊又像少了信息。
     * ⚠️ 它比 `reachedText` 可信:实测同一条 status='已回访' 而 result='诊后回访未接'。
     *   前者只说明任务被标记过,后者才是真的联系结果。⇒ **有它就以它为准**。
     */
    resultText: string | null;
    /**
     * 联系上没有(已回访 / 未回访)—— **只在结果和主题都为空时**当兜底,且它并不可靠(见上)。
     *
     * ⛔⛔ **刻意不给 `taskStatus`(已完成 / 未完成)**,⛔ 别"补全"回来:
     *   ① 那是内部流程状态,客服打电话前用不上(产品 2026-08-06:
     *      「回访任务已完成就不要说了,总结备注里的事实就行」);
     *   ② 两个状态一起喂会让摘要说「已联系且任务已完成」,而详情页「历史联系」卡
     *      按 taskStatus 说「未完成」—— 两句都对却像自相矛盾(实测 2026-08-05 王利)。
     *   不报流程状态,这个矛盾从根上就不存在。
     */
    reachedText: string | null;
  }>;
  /**
   * **关系亲密度** —— 第②句的另一半(产品 2026-08-04 评审原话:
   * 「第二句话是他跟我们以前的关系亲密度是什么样的,**就是联系他多不多**」)。
   *
   * ⚠️ 上面的 `returnVisitHistory` 只给**最近 3 条**(喂全量会撑爆上下文,也没必要)——
   *   所以"多不多"必须单独给个数,⛔ 别让模型去数那 3 条推总量,那必然低估。
   * ⚠️ 只统计**已发生**的,与 returnVisitHistory 同一条判据(未来的预约回访不算联系过)。
   */
  contactStats: {
    /// 已发生的回访总条数;0 = 从没联系过
    total: number;
    /// 最早一次距今(如 3 年前);total=0 时为 null
    sinceText: string | null;
  };
  /**
   * 🔴 **被挂断过几次** —— 回访数据里**唯一有据**的"不好的体验"。0 = 没有记录。
   *
   * ── 为什么只有这一个(2026-08-07 全量查证)──────────────────────
   * 产品要第②句带上「有没有过不好的体验」,而这一句的依据只看回访数据。实测:
   *   · `type` 只有三种:常规回访 / 咨询回访 / 术后回访 —— **没有投诉类**;
   *   · `result` 是自由文本(3 万行 6765 种取值),扫遍长尾:
   *     投诉 / 不满 / 纠纷 / 态度 / 退费 / 抱怨 —— **一条都没有**;
   *     真正的负面只有**「挂断」一种形态**(688 次 / 595 位患者,占有回访记录患者的 11.5%)。
   *   ⚠️ 第一遍扫的时候「无不适」被"不适"这个词接住了,假阳性 5816 条 ——
   *     负面词表必须排除否定式(无不适 / 无异常 / 不疼),⛔ 别再照着直觉拍词表。
   *
   * ⭐ 「挂断」是**关系信号**不是接通率:患者接了、听出是谁、然后把电话挂了。
   *   与 EXECUTION_OUTCOME 里的「秒挂」是同一件事(那边已判为不成功类 + 30 天抑制)。
   * ⛔⛔ **没被挂断 ≠ 关系好**,所以 0 的时候第②句**什么都不说**,
   *   ⛔ 绝不许输出「没有不良记录」「关系良好」—— 那是拿"没证据"当"反面"用,
   *   与 noTag 那条铁律是同一件事(违了就是凭空造事实)。
   * ⛔ 也不许拿「未接」当不好的体验:没接通的原因太多(换号 / 在忙 / 拦截),那是推测不是事实。
   */
  hangUpCount: number;
  /**
   * 本批次福利(第④点的最高优先勾子)—— 主管在分配时写的文案原文;没配 = null。
   *
   * ⚠️ 只在批次仍 `confirmed` 时给(与话术带福利同一个判据,⛔ 别另立标准)。
   * ⛔ **只能说这段原文包含的内容**:不得追加条件/期限/名额,不得夸大 —— 与话术同一条硬约束。
   */
  benefitText: string | null;
}

/**
 * 四点简报(2026-08-05 从一句话拆开)。
 *
 * ⚠️ **逻辑没变,只是输出形态变了** —— 四点各自回答的还是原来那四问,
 *   输入要素、优先级(医生原话 > 复查锚点)、防编造约束全部照旧。
 * ⚠️ 每点都**必须有内容**:没有事实时如实说"暂无",⛔ 不许空串
 *   (空串会让界面出现一个没头没尾的空行,比一句"暂无"更让人困惑)。
 */
export interface DraftRecallBriefOutput {
  /** ① 患者是谁 —— 画像一句话(生命周期/价值/人群) */
  who: string;
  /** ② 以前怎么联系的 —— 回访历史一句话;从没联系过就说"此前无回访记录" */
  history: string;
  /** ③ 解决什么 + 不处理的后果 —— 一句话 */
  problem: string;
  /** ④ 怎么开口 —— 切入勾子;优先级:批次福利 > 医生原话 > 复查锚点 */
  hook: string;
}
