import { Injectable } from '@nestjs/common';
import type { AiCall } from '../../ai-call.interface';
import type { SafetyRule } from '../../core/safety-gate.service';
import { DraftPlanSummarySchema } from './schema';
import type { DraftPlanSummaryInput, DraftPlanSummaryOutput } from './input.types';
import {
  DRAFT_PLAN_SUMMARY_PROMPT_VERSION,
  DRAFT_PLAN_SUMMARY_SYSTEM,
  buildDraftPlanSummaryPrompt,
} from './prompt';

/**
 * Safety:跟 script 一套禁词检查,但 summary 偶尔需要客观描述"患者拒绝"等,
 * 所以禁词列表更保守,避免误杀。
 */
const FORBIDDEN_PHRASES = ['一定能', '保证', '绝对', '百分百', '100%', '亲爱的'];

const safetyRules: ReadonlyArray<SafetyRule<DraftPlanSummaryOutput>> = [
  {
    name: 'no_forbidden_phrases',
    severity: 'block',
    check(output) {
      const fullText = [output.onePage, output.medicalRecord, output.treatmentChain].join('\n');
      const hit = FORBIDDEN_PHRASES.filter((p) => fullText.includes(p));
      return {
        pass: hit.length === 0,
        message: hit.length > 0 ? `命中禁词: ${hit.join(',')}` : undefined,
      };
    },
  },
];

/**
 * Fallback — LLM 失败时用 input 拼最朴素的版本。
 * 比 script 简单:facts/chains 直接列出来,客服自己看。
 */
function fallback(input: DraftPlanSummaryInput): DraftPlanSummaryOutput {
  const { patient, plan, facts, chains, clinicName } = input;
  const reasonText = plan.reasons[0]?.reason ?? plan.primaryScenarioLabel;

  const onePage = `**${patient.nameMasked}**(${patient.gender ?? '?'}·${patient.age ?? '?'}岁) — 来自 ${clinicName}\n\n本次召回原因:${reasonText}\n\nAI 总结暂未生成,请参考下方"病历"与"治疗链"两段。`;

  const factGroups: Record<string, typeof facts> = {};
  for (const f of facts) {
    if (!factGroups[f.type]) factGroups[f.type] = [];
    factGroups[f.type]!.push(f);
  }
  const medicalRecord =
    Object.entries(factGroups)
      .map(([type, fs]) => {
        const lines = fs
          .slice(0, 6)
          .map((f) => `- ${f.occurredAt?.slice(0, 10) ?? '?'} ${f.title ?? f.summary ?? type}`)
          .join('\n');
        return `### ${type}\n${lines}`;
      })
      .join('\n\n') || '(无病历数据)';

  const treatmentChain =
    chains.length > 0
      ? chains
          .map(
            (c) =>
              `### ${c.name} [${c.status}]\n当前阶段 ${c.currentStage};共 ${c.nodes.length} 个节点。`,
          )
          .join('\n\n')
      : '(无治疗链数据)';

  return { onePage, medicalRecord, treatmentChain };
}

@Injectable()
export class DraftPlanSummaryCall
  implements AiCall<DraftPlanSummaryInput, DraftPlanSummaryOutput>
{
  readonly kind = 'summary' as const;
  readonly callKey = 'draft_plan_summary';
  readonly promptVersion = DRAFT_PLAN_SUMMARY_PROMPT_VERSION;
  readonly defaultModelId = 'deepseek-v4-flash';
  readonly outputSchema = DraftPlanSummarySchema;
  readonly safetyRules = safetyRules;

  buildPrompt(input: DraftPlanSummaryInput) {
    return {
      system: DRAFT_PLAN_SUMMARY_SYSTEM,
      prompt: buildDraftPlanSummaryPrompt(input),
    };
  }

  fallback(input: DraftPlanSummaryInput): DraftPlanSummaryOutput {
    return fallback(input);
  }
}
