import { Injectable, Logger } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../../../prisma/prisma.service';

/**
 * InvocationRecorder — 落 agent_invocations 表(harness 唯一真理源)。
 *
 * 一次 AI 调用对应一行(包括 cached 也写一行,evalMode 'production' + status 'cached')。
 * judge 调用对应一行(kind='judge', parentInvocationId 指向被评的那条)。
 *
 * start → end 二段提交:start 写 status=running,end 回填 output + tokens + status。
 * 失败也要落(status=failed + errorMessage),用于排查 + 失败率统计。
 */

export interface InvocationStartInput {
  hostId: string;
  tenantId: string;
  kind: string;                // 'script' | 'summary' | 'persona_feature' | 'judge' ...
  callKey: string;
  promptVersion: string;
  modelProvider: string;       // 'deepseek' | 'qwen' ...
  modelName: string;           // 'deepseek-v4-pro'
  workflowRunId: string;
  parentInvocationId?: string;
  inputHash: string;
  cacheHit?: boolean;
  evalMode?: 'production' | 'golden_set' | 'shadow' | 'manual';
  goldenSetCaseId?: string;
  inputSnapshot: Prisma.InputJsonValue;
  promptTemplate?: string;     // user prompt(截断 8000)
  systemPrompt?: string;       // system prompt(base-system + 注入的 skills 正文;审计/复盘用)
  linkedPatientId?: string;
  linkedPersonaId?: string;
  linkedPlanId?: string;
}

export interface InvocationEndInput {
  output?: Prisma.InputJsonValue;
  outputText?: string;
  promptTokens?: number;
  completionTokens?: number;
  totalTokens?: number;
  /** 输入里命中 vendor prompt cache 的 tokens(DeepSeek prompt_cache_hit_tokens)*/
  cachedInputTokens?: number;
  /** 输出里 reasoning 部分(thinking 模式),completionTokens 已含,这里冗余 */
  reasoningTokens?: number;
  costYuan?: number;
  /** 首字延迟(ms)— 仅流式有意义,非流式不传(留 null)*/
  ttftMs?: number;
  /** 生成总耗时(ms)— LLM 调用净时长 */
  latencyMs?: number;
  status: 'succeeded' | 'failed' | 'cached';
  errorMessage?: string;
}

@Injectable()
export class InvocationRecorderService {
  private readonly logger = new Logger(InvocationRecorderService.name);

  constructor(private readonly prisma: PrismaService) {}

  async start(input: InvocationStartInput): Promise<string> {
    const row = await this.prisma.agentInvocation.create({
      data: {
        hostId: input.hostId,
        tenantId: input.tenantId,
        kind: input.kind,
        callKey: input.callKey,
        promptVersion: input.promptVersion,
        modelProvider: input.modelProvider,
        modelName: input.modelName,
        workflowRunId: input.workflowRunId,
        parentInvocationId: input.parentInvocationId ?? null,
        inputHash: input.inputHash,
        cacheHit: input.cacheHit ?? false,
        evalMode: input.evalMode ?? 'production',
        goldenSetCaseId: input.goldenSetCaseId ?? null,
        inputSnapshot: input.inputSnapshot,
        promptTemplate: input.promptTemplate ?? null,
        systemPrompt: input.systemPrompt ?? null,
        linkedPatientId: input.linkedPatientId ?? null,
        linkedPersonaId: input.linkedPersonaId ?? null,
        linkedPlanId: input.linkedPlanId ?? null,
        status: input.cacheHit ? 'cached' : 'running',
      },
      select: { id: true },
    });
    return row.id;
  }

  async end(invocationId: string, input: InvocationEndInput): Promise<void> {
    await this.prisma.agentInvocation.update({
      where: { id: invocationId },
      data: {
        output: input.output ?? Prisma.JsonNull,
        outputText: input.outputText ?? null,
        promptTokens: input.promptTokens ?? 0,
        completionTokens: input.completionTokens ?? 0,
        totalTokens: input.totalTokens ?? 0,
        cachedInputTokens: input.cachedInputTokens ?? 0,
        reasoningTokens: input.reasoningTokens ?? 0,
        costYuan: input.costYuan ?? 0,
        ttftMs: input.ttftMs ?? null,
        latencyMs: input.latencyMs ?? null,
        status: input.status,
        errorMessage: input.errorMessage ?? null,
        endedAt: new Date(),
      },
    });
  }

  /**
   * judge 评分回填到被评 invocation(不新增字段那条记录,而是更新被评的那条)。
   * judge 自身也单独走 start/end 落一行,kind='judge', parentInvocationId 指向被评。
   */
  async attachJudge(
    targetInvocationId: string,
    judgeScore: number,
    judgeRubric: Prisma.InputJsonValue,
  ): Promise<void> {
    await this.prisma.agentInvocation.update({
      where: { id: targetInvocationId },
      data: { judgeScore, judgeRubric },
    });
  }
}
