import { Injectable } from '@nestjs/common';
import type { AiCall } from '../../ai-call.interface';
import type { SafetyRule } from '../../core/safety-gate.service';
import { DraftRecallBriefSchema } from './schema';
import type { DraftRecallBriefInput, DraftRecallBriefOutput } from './input.types';
import {
  DRAFT_RECALL_BRIEF_PROMPT_VERSION,
  DRAFT_RECALL_BRIEF_SYSTEM,
  buildDraftRecallBriefPrompt,
} from './prompt';

const FORBIDDEN_PHRASES = ['一定能', '保证', '绝对', '百分百', '100%', '亲爱的'];

const safetyRules: ReadonlyArray<SafetyRule<DraftRecallBriefOutput>> = [
  {
    name: 'no_forbidden_phrases',
    severity: 'block',
    check(output) {
      // 四段拼起来一起扫 —— ⛔ 别只扫 problem:禁词出现在 hook 里同样会被客服念出去
      const all = [output.who, output.history, output.problem, output.hook].join(' ');
      const hit = FORBIDDEN_PHRASES.filter((p) => all.includes(p));
      return { pass: hit.length === 0, message: hit.length > 0 ? `命中禁词: ${hit.join(',')}` : undefined };
    },
  },
];

/**
 * LLM 失败 / safety 拒收时:用召回原因拼**四段**最朴素的简报。
 * ⚠️ 四段都要有内容 —— 空串会让界面出现没头没尾的空行(见 schema 注释)。
 */
function fallback(input: DraftRecallBriefInput): DraftRecallBriefOutput {
  const r = input.reasons[0];
  const who = input.persona.length > 0 ? input.persona.map((p) => p.value).slice(0, 2).join('、') : '—';
  const history =
    input.returnVisitHistory.length > 0
      ? `${input.returnVisitHistory[0]!.atText}${input.returnVisitHistory[0]!.type ?? '回访'}`
      : '此前无回访记录';
  if (!r) return { who, history, problem: '暂无明确召回原因', hook: '可约复查请医生评估' };

  const cats = r.expectedCategories.join(' / ');
  const problem = [
    r.subLabel,
    [r.tooth ? `${r.tooth}` : null, r.diagnosis, `${r.daysSinceText}前`, cats ? `未启动 ${cats}` : null]
      .filter(Boolean)
      .join(' · '),
  ]
    .filter(Boolean)
    .join(':');

  // 切入(第④点,按优先级):本批福利 > 医生计划/建议/医嘱(带原话) > 洁牙锚点(带时间) > 通用复查邀约
  const g0 = input.doctorGuidance.find((g) => g.verbatim) ?? input.doctorGuidance[0];
  const KIND_VERB = { plan: '已有计划', recommendation: '建议过', advice: '医嘱交代过' } as const;
  const hook = input.benefitText?.trim()
    ? `本批有「${input.benefitText.trim()}」,可约复查跟进`
    : g0
      ? `医生${KIND_VERB[g0.kind]}${g0.verbatim ? `「${g0.verbatim}」` : g0.label},可约复查跟进`
      : input.reviewAnchors.lastCleaningText
        ? `距上次洁牙/检查 ${input.reviewAnchors.lastCleaningText},可约复查切入`
        : '可约复查请医生评估';

  return { who, history, problem, hook };
}

@Injectable()
export class DraftRecallBriefCall
  implements AiCall<DraftRecallBriefInput, DraftRecallBriefOutput>
{
  readonly kind = 'summary' as const;
  readonly callKey = 'draft_recall_brief';
  readonly promptVersion = DRAFT_RECALL_BRIEF_PROMPT_VERSION;
  readonly defaultModelId = 'qwen'; // 这类一句话摘要统一走 Qwen(qwen3.7-max)
  readonly outputSchema = DraftRecallBriefSchema;
  readonly safetyRules = safetyRules;

  buildPrompt(input: DraftRecallBriefInput) {
    return {
      system: DRAFT_RECALL_BRIEF_SYSTEM,
      prompt: buildDraftRecallBriefPrompt(input),
    };
  }

  fallback(input: DraftRecallBriefInput): DraftRecallBriefOutput {
    return fallback(input);
  }
}
