import { Injectable, Logger } from '@nestjs/common';
import { AiCallRunnerService } from '../../../../ai-call-runner.service';
import type { AiCallContext } from '../../../../ai-call.interface';
import type { ScriptContext } from '../../shared/input.types';
import { machineSafetyScan } from '../../shared/safety-rules';
import { stableTemplateFallback } from '../stable/stable.call';
import { DeepPlanCall, DeepWriteCall, DeepVerifyCall } from './calls';
import { draftOutputToDeep, type DeepDraft, type DeepPlan, type DeepVerifyIssue } from './types';

export interface DeepScriptResult {
  draft: DeepDraft;
  source: 'agent' | 'template_fallback';
  invocationId: string;
  cacheHit: boolean;
  costYuan: number;
  promptTokens: number;
  completionTokens: number;
  fallbackReason?: string;
  stepsRun: string[];
}

/**
 * DeepScriptStrategy —— 深度档 3 步编排(脊柱仍是 AiCallRunner,每步各落 agent_invocations)。
 *   规划(plan) → 写(write,多段) → 独立对抗校验(verify) → 不过则 repair(≤1 轮) → 仍不过则稳健兜底。
 * 机器安全(禁词/承诺/加粗时间)在策略侧硬扫(machineSafetyScan),与 LLM 校验合并决定是否 repair。
 */
@Injectable()
export class DeepScriptStrategy {
  private readonly logger = new Logger(DeepScriptStrategy.name);
  private readonly MAX_REPAIR = 1;

  constructor(
    private readonly runner: AiCallRunnerService,
    private readonly planCall: DeepPlanCall,
    private readonly writeCall: DeepWriteCall,
    private readonly verifyCall: DeepVerifyCall,
  ) {}

  async run(ctx: ScriptContext, runCtx: AiCallContext): Promise<DeepScriptResult> {
    const steps: string[] = [];
    let cost = 0;
    let promptTokens = 0;
    let completionTokens = 0;
    const acc = (r: { costYuan: number; promptTokens: number; completionTokens: number }) => {
      cost += r.costYuan;
      promptTokens += r.promptTokens;
      completionTokens += r.completionTokens;
    };

    // ── 步骤1:规划(best-effort;失败用退化大纲,不阻断) ──
    let plan: DeepPlan;
    try {
      const r = await this.runner.run(this.planCall, ctx, runCtx);
      acc(r);
      plan = r.output;
      steps.push('plan');
    } catch (err) {
      this.logger.warn(`deep plan 失败,用退化大纲: ${(err as Error).message}`);
      plan = degeneratePlan();
      steps.push('plan:degenerate');
    }

    // ── 步骤2:写(有 fallback,必返回) ──
    let w = await this.runner.run(this.writeCall, { ctx, plan }, runCtx);
    acc(w);
    let draft = w.output;
    let invocationId = w.invocationId;
    steps.push(w.source === 'template_fallback' ? 'write:fallback' : 'write');
    if (w.source === 'template_fallback') {
      return this.done(draft, 'template_fallback', invocationId, cost, promptTokens, completionTokens, steps, w.fallbackReason);
    }

    // ── 步骤3:独立对抗校验 + 机器扫 ──
    const issues: DeepVerifyIssue[] = machineScanIssues(draft);
    try {
      const v = await this.runner.run(this.verifyCall, { ctx, draft }, runCtx);
      acc(v);
      steps.push('verify');
      if (!v.output.pass) issues.push(...v.output.issues);
    } catch (err) {
      this.logger.warn(`deep verify 失败,仅依据机器扫: ${(err as Error).message}`);
      steps.push('verify:skip');
    }

    // ── repair(≤1 轮) ──
    if (issues.length > 0) {
      this.logger.debug(`deep repair: ${issues.length} 个 issue`);
      const w2 = await this.runner.run(this.writeCall, { ctx, plan, repairIssues: issues }, runCtx);
      acc(w2);
      draft = w2.output;
      invocationId = w2.invocationId;
      steps.push(w2.source === 'template_fallback' ? 'repair:fallback' : 'repair');
      // 终检:机器闸仍不过 → 稳健兜底(对抗哲学:接地/安全宁兜底不放行)
      const stillBad = machineSafetyScan(joinDraft(draft));
      if (w2.source === 'template_fallback' || stillBad.length > 0) {
        const fb = draftOutputToDeep(stableTemplateFallback(ctx));
        return this.done(fb, 'template_fallback', invocationId, cost, promptTokens, completionTokens, steps, `repair 后仍不过: ${stillBad.join(';')}`);
      }
    }

    return this.done(draft, 'agent', invocationId, cost, promptTokens, completionTokens, steps);
  }

  private done(
    draft: DeepDraft,
    source: 'agent' | 'template_fallback',
    invocationId: string,
    costYuan: number,
    promptTokens: number,
    completionTokens: number,
    stepsRun: string[],
    fallbackReason?: string,
  ): DeepScriptResult {
    this.logger.log(`deep done source=${source} steps=[${stepsRun.join('→')}] cost=${costYuan.toFixed(4)}`);
    return { draft, source, invocationId, cacheHit: false, costYuan, promptTokens, completionTokens, fallbackReason, stepsRun };
  }
}

/** 把整篇草稿拼成纯文本(机器安全扫用) */
function joinDraft(draft: DeepDraft): string {
  return draft.sections.map((s) => s.markdown).join('\n');
}

/** 机器扫 → issue 列表(回喂 writer repair) */
function machineScanIssues(draft: DeepDraft): DeepVerifyIssue[] {
  return machineSafetyScan(joinDraft(draft)).map((p) => ({
    section: '(机器安全闸)',
    problem: p,
    fix: '删除该表述 / 价格时间改成不承诺、用【时间段】占位',
  }));
}

/** 规划失败时的退化大纲(4 段;写步仍能产出) */
function degeneratePlan(): DeepPlan {
  return {
    sections: [
      { key: 'opening', title: '开场问候', intent: '称呼+自报家门+医生关怀+问近况', points: ['用给定称呼/自报家门/诊断医生/智能时间'] },
      { key: 'missed', title: '切入本次问题', intent: '温和带出本次应治未治项', points: ['结合病历讲隐患与趁早处理的好处'] },
      { key: 'review', title: '复查邀约', intent: '邀约来院复查本次问题', points: ['用【时间段】引导预约'] },
      { key: 'close', title: '结束', intent: '预约成功/不成功两种收尾', points: ['简短有温度,时间用【具体预约时间】占位'] },
    ],
  };
}
