import { Injectable, Logger } from '@nestjs/common';
import type { AiCall } from '../../../../ai-call.interface';
import type { SafetyRule } from '../../../../core/safety-gate.service';
import { StandardScriptSchema } from './schema';
import type { DraftPlanScriptInput } from '../../shared/input.types';
import { buildStandardScriptPrompt } from './prompt';
import { composeSystem } from '../../shared/skill-composer';
import { DraftPlanScriptSkillRegistry } from '../../shared/skill-registry.service';
import { SCRIPT_SAFETY_RULES_SECTIONS } from '../../shared/safety-rules';
import { stableTemplateFallback } from '../stable/stable.call';
import { type DeepDraft, draftOutputToDeep } from '../deep/types';

/**
 * StandardScriptCall —— 标准档(投入档之二)。
 *
 * 与稳健档共用脊柱(AiCallRunner)、输入(ScriptContext)、兜底(稳健模板);**差异**:
 *   - 输出形态:**去模板 = 自由 `sections[]`(固定 4 段)**,不绑 opening/informMissed/… 角色
 *     (与深度档 DeepDraft 同形;区别仅:标准=单步 + 固定 4 段,深度=3 步 pipeline + 段数不限)。
 *   - system:composeSystem(input, skills, 'standard') → common + format.standard + 人群共性
 *   - user:buildStandardScriptPrompt(厚病历接地,病种只给名,朴素 labeled facts 无 {} 占位)
 *   - safety:SCRIPT_SAFETY_RULES_SECTIONS(段数组形态);兜底:稳健模板转 4 段数组(draftOutputToDeep)
 *
 * callKey 仍用 'draft_plan_script'(同一逻辑调用),档位差异落 promptVersion → eval 可按版本切档对比。
 */
const STANDARD_PROMPT_VERSION = 'draft_plan_script@2026-06-08-standard-v17'; // v17: 低龄约束(<18 不主推种植)+ 替牙期(≤12)说「早期矫治」。v16: 高龄硬约束(≥75 缺牙禁推种植/手术)。v15: 自报家门去人名 → 占位【回访客服】(渲染期按登录人回填,修串名 bug)。v14: schema 去硬长度/段数约束(.min/.max/.length → describe 软引导);v13: 加画像精简版(⚠禁忌/治疗敏感/特别关注 + rfm/生命周期定语气;切入点留深度档);v11: 病历补全(治疗计划=plannedTreatments 结构化 + 一般情况/处置/诊断说明,对齐页面 emr-soap);v10: format.md 去污染(删 tier 名/去模板对比/见 common.md 等解释性 meta,纯指令);流式输出(段数组 partial 边出边渲染);v9: 真去模板 — 4 固定角色字段 → 自由 sections[];v8: 去 {} 替换占位;v7: format 瘦身 + opening 放开 + closing 软化

@Injectable()
export class StandardScriptCall implements AiCall<DraftPlanScriptInput, DeepDraft> {
  private readonly logger = new Logger(StandardScriptCall.name);
  readonly kind = 'script' as const;
  readonly callKey = 'draft_plan_script';
  readonly promptVersion = STANDARD_PROMPT_VERSION;
  readonly defaultModelId = 'deepseek-v4-flash';
  readonly outputSchema = StandardScriptSchema;
  // 显式标 SafetyRule<DeepDraft>,让 runner.run 的 TOutput 推成 DeepDraft(否则会被结构型窄化丢 tone)
  readonly safetyRules: ReadonlyArray<SafetyRule<DeepDraft>> = SCRIPT_SAFETY_RULES_SECTIONS;

  constructor(private readonly skillRegistry: DraftPlanScriptSkillRegistry) {}

  buildPrompt(input: DraftPlanScriptInput) {
    const composed = composeSystem(input, this.skillRegistry.getAllSkills(), 'standard');
    if (composed.matchedSkills.length === 0) {
      this.logger.warn(
        `[standard] compose 0 个 skill 命中(scenario=${composed.context.scenario}, ` +
          `dx=${composed.context.diagnosisCodes.join(',')}, pop=${composed.context.population}) — 仅 base`,
      );
    } else {
      this.logger.debug(
        `[standard] compose skills: ${composed.matchedSkills.map((s) => s.frontmatter.name).join(', ')} ` +
          `(hash=${composed.composeHash})`,
      );
    }
    return {
      system: composed.systemPrompt,
      prompt: buildStandardScriptPrompt(input),
    };
  }

  // 标准档失败 → 回退稳健模板(4 段),转成自由段数组形态(安全网,客服永远有东西可用)
  fallback(input: DraftPlanScriptInput): DeepDraft {
    return draftOutputToDeep(stableTemplateFallback(input));
  }
}
