/**
 * 宠物神经系统 — 语义事件总线 + 演出脚本契约。
 *
 * 三层架构(感知 → 大脑 → 身体,单向):
 *   感知层:业务代码/传感器只 emitPetEvent(语义事件),不关心宠物怎么演;
 *   大脑层:pet-brain 订阅事件,经"导演"产出 DirectorScript;
 *   身体层:pet-body 纯展示,按姿态渲染。
 *
 * DirectorScript 是大脑的唯一指令入口:现在由规则导演(模板)产出;
 * 将来 LLM 导演产同构 JSON(受限词表 + zod 校验)走同一入口 —— 主流程确定性,
 * LLM 只补台词,断了即退回纯规则(对齐 PAC 原则 5:Agent 路径必须可降级)。
 */

/** 动作词表(受限枚举,LLM 导演也只能从这里选,不许自由发挥)。 */
export type PetGesture =
  | 'none'
  | 'think'
  | 'celebrate'
  | 'greet'
  | 'sleep'
  | 'floss' // 牙线自理
  | 'shine' // 闪亮微笑(牙面 ding 一下)
  | 'chomp' // 咬合操(张合嘴)
  // 牙医组合派生动作(检查 → 治疗 → 处置)
  | 'vet_exam' // 看牙检查(戴额镜 + 口镜)
  | 'vet_scale' // 洗牙(超声洁治 + 水珠)
  | 'vet_fill' // 补牙(电钻 + 火星)
  | 'vet_polish' // 抛光(抛光杯 + ✨)
  | 'vet_fluoride' // 涂氟(applicator,随后获护盾)
  | 'absorb'; // 吸入:一批患者被"吸"进助手(移交人群时演)

/** 语义事件 — 感知层发,大脑订阅。 */
export type PetEvent =
  | { type: 'ai_thinking_start' }
  | { type: 'ai_thinking_end' }
  /** 直接递一段演出脚本给大脑(规则模块现在用,LLM 导演将来发同构事件) */
  | { type: 'director'; script: DirectorScript }
  /** 宠物开口说话的三个阶段:think=已发起 LLM 还没吐第一个词 / talk=流式中(嘴动) / end=说完 */
  | { type: 'pet_voice'; phase: 'think' | 'talk' | 'end' }
  /** 大脑 idle 轮盘抽中"牙医组合"→ 通知 widget 跑诊疗派生链 */
  | { type: 'vet_combo' }
  /**
   * 把一批患者移交给助手(主管在召回池点「移交助手」)。
   * ⚠️ 业务代码只发这个**语义**事件,不描述"怎么演" —— 演法归 pet-brain。
   * 将来换演法(比如改成传送带动画)不用动分配功能的任何一行。
   */
  | { type: 'cohort_handoff'; payload: { count: number; treatment?: string } };

/** 演出脚本 — 现在规则导演用,将来 LLM 导演同入口。 */
export interface DirectorScript {
  gesture?: PetGesture;
  /** 唯一自由文本:气泡台词(LLM 的本职就在这一个字段)。 */
  bubble?: string;
  /** 演出时长,到点自动复位回基线姿态。 */
  ttlMs?: number;
}

type Listener = (e: PetEvent) => void;
const listeners = new Set<Listener>();

export function emitPetEvent(e: PetEvent): void {
  listeners.forEach((l) => l(e));
}

export function onPetEvent(l: Listener): () => void {
  listeners.add(l);
  return () => listeners.delete(l);
}
