import { Injectable } from '@nestjs/common';
import { PlanScriptOrchestrator } from '../ai/orchestrators/plan-script.orchestrator';
import { DraftPlanScriptSkillRegistry } from '../ai/calls/draft-plan-script/shared/skill-registry.service';
import { composeSystem } from '../ai/calls/draft-plan-script/shared/skill-composer';
import type { DraftPlanScriptInput } from '../ai/calls/draft-plan-script/shared/input.types';
import { toothFriendly } from '../ai/calls/draft-plan-script/shared/script-facts';
import {
  REALTIME_COACH_ROLE_HEADER,
  REALTIME_COACH_KNOWLEDGE_HEADER,
  REALTIME_COACH_OUTPUT_CONSTRAINTS,
} from './coach-prompts';

/**
 * RealtimeCoachContextService — 给 Qwen-Omni-Realtime 装配 instructions。
 *
 * 复用的是【患者上下文装配管线】(PlanScriptOrchestrator.buildScriptInputForPlan + composeSystem
 * 选中的 skills 列表),**但不注入 skill body** —— PAC 的 skills 本质是"邀约到店"的召回销售脚本,
 * 拿来当教练知识会把它变成推销机器(实测如此)。教练只需:患者临床事实 + 人情味教练指令;
 * 牙科常识模型自带。composeSystem 仍用于得出"哪些 skills 相关"(给面板展示 skills tag)。
 */
@Injectable()
export class RealtimeCoachContextService {
  constructor(
    private readonly planScriptOrchestrator: PlanScriptOrchestrator,
    private readonly skillRegistry: DraftPlanScriptSkillRegistry,
  ) {}

  async buildInstructions(planId: string): Promise<{ instructions: string; skills: string[] }> {
    const input = await this.planScriptOrchestrator.buildScriptInputForPlan(planId);
    const composed = composeSystem(input, this.skillRegistry.getAllSkills(), 'stable');

    // skill body 作为"专业知识"注入,但排除 scenario-*(那是"一步到位约到店"的召回剧本,
    // 会盖过"抓主诉、循序渐进"的口径);保留 diagnosis / objection / relationship / population / safety。
    const knowledgeBlock = composed.matchedSkills
      .filter((s) => !s.frontmatter.name.startsWith('scenario-'))
      .map((s) => `## [${s.frontmatter.name}]\n${s.body}`)
      .join('\n\n---\n\n');

    const instructions = [
      REALTIME_COACH_ROLE_HEADER,
      this.buildPatientContextBlock(input),
      knowledgeBlock ? `${REALTIME_COACH_KNOWLEDGE_HEADER}\n\n${knowledgeBlock}` : '',
      REALTIME_COACH_OUTPUT_CONSTRAINTS, // 输出约束放最后,最近最强
    ]
      .filter(Boolean)
      .join('\n\n');

    return {
      instructions,
      skills: composed.matchedSkills.map((s) => s.frontmatter.name),
    };
  }

  private buildPatientContextBlock(input: DraftPlanScriptInput): string {
    const cc = input.clinicalContext;
    const persona = input.personaHighlights.map((h) => `${h.label}:${h.description}`).join(' · ');
    // 结构化 → 散文(边界渲染;ScriptContext 里只存结构,这里拼成口语供教练 LLM 取用)
    const pending = cc.pendingTreatments
      .map((p) => (p.toothPositions.length ? `${p.label} · ${toothFriendly(p.toothPositions)}` : p.label))
      .join('、');
    const lastVisit = cc.lastVisit
      ? `${cc.lastVisit.date ?? '近期'} — ${cc.lastVisit.summary ?? '到店就诊'}`
      : '';
    const recent = cc.recentTreatments
      .map((t) =>
        [`做过${t.categoryLabel}`, t.subtype ? `上次 ${t.subtype}` : '', t.doctorName ? `${t.doctorName}医生` : '', t.date ?? '']
          .filter(Boolean)
          .join(' · '),
      )
      .join(' / ');
    return [
      '# 当前通话患者背景(真实数据,供你聊天时取用,不要编造、别照着推销)',
      `- 诊所:${input.clinicName} · 称呼:${input.patient.salutation} · 年龄:${input.patient.age ?? '未知'}`,
      persona ? `- 画像:${persona}` : '',
      `- 上次就诊发现 / 待处理:${pending || input.plan.reasons[0]?.reason || '(无)'}`,
      `- 主治医生:${cc.primaryDoctorName ?? '(未知)'} · 距上次到店:${cc.daysSinceLastVisit ?? '未知'} 天`,
      lastVisit ? `- 上次到店:${lastVisit}` : '',
      recent ? `- 近期做过的治疗:${recent}` : '',
      `- 老客/新客:已完成 ${cc.completedTreatmentCount} 次治疗`,
    ]
      .filter(Boolean)
      .join('\n');
  }
}
