/**
 * Session Recorder — 会话录制器（TRACE 层）
 *
 * 负责将完整的会话执行轨迹写入 session_*.jsonl 文件。
 * 每行一条 JSON 事件，支持事后回放和分析。
 *
 * 日志格式：SSE-like JSON Lines
 * 事件类型：session_start, context_built, iteration_start, llm_request,
 *          llm_response, tool_call, tool_result, iteration_end, session_end
 *
 * @see docs/MetonaAI-Desktop 架构与交互设计.html — 全链路透明可追踪
 * @see docs/生产级通用 AI Agent 智能体桌面应用：完整设计与构建指南.html — 第十一章
 */

import { join } from 'path';
import { appendFileSync, existsSync, mkdirSync } from 'fs';
import log from 'electron-log';

// ===== 事件类型 =====

export type TraceEventType =
  | 'session_start'
  | 'context_built'
  | 'iteration_start'
  | 'llm_request'
  | 'llm_response'
  | 'tool_call'
  | 'tool_result'
  | 'iteration_end'
  | 'session_end';

export interface TraceEvent {
  seq: number;
  ts: string;
  event: TraceEventType;
  sessionId: string;
  [key: string]: unknown;
}

// ===== 服务类 =====

export class SessionRecorder {
  private filePath: string | null = null;
  private seq = 0;
  private sessionId: string | null = null;

  constructor(private workspacePath: string) {}

  /**
   * 开始录制会话
   */
  startRecording(sessionId: string): void {
    this.sessionId = sessionId;
    this.seq = 0;

    const logsDir = join(this.workspacePath, 'logs');
    if (!existsSync(logsDir)) {
      mkdirSync(logsDir, { recursive: true });
    }

    const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
    this.filePath = join(logsDir, `session_${sessionId}_${timestamp}.jsonl`);

    // 写入 session_start 事件
    this.writeEvent({
      event: 'session_start',
      sessionId,
      workspace: this.workspacePath,
    });

    log.info(`Session recording started: ${this.filePath}`);
  }

  /**
   * 停止录制
   */
  stopRecording(params: {
    totalIterations: number;
    totalTokens: number;
    durationMs: number;
    terminationReason: string;
  }): void {
    if (!this.sessionId || !this.filePath) return;

    this.writeEvent({
      event: 'session_end',
      sessionId: this.sessionId,
      totalIterations: params.totalIterations,
      totalTokens: params.totalTokens,
      durationMs: params.durationMs,
      terminationReason: params.terminationReason,
    });

    log.info(`Session recording stopped: ${this.filePath}`);
    this.filePath = null;
    this.sessionId = null;
  }

  /**
   * 记录上下文构建
   */
  recordContextBuilt(params: { tokenCount: number; usageRatio: number }): void {
    this.writeEvent({
      event: 'context_built',
      sessionId: this.sessionId!,
      tokens: params.tokenCount,
      ratio: params.usageRatio,
    });
  }

  /**
   * 记录迭代开始
   */
  recordIterationStart(iteration: number): void {
    this.writeEvent({
      event: 'iteration_start',
      sessionId: this.sessionId!,
      iteration,
    });
  }

  /**
   * 记录 LLM 请求
   */
  recordLLMRequest(params: {
    iteration: number;
    provider: string;
    model: string;
    messageCount: number;
  }): void {
    this.writeEvent({
      event: 'llm_request',
      sessionId: this.sessionId!,
      iteration: params.iteration,
      provider: params.provider,
      model: params.model,
      messageCount: params.messageCount,
    });
  }

  /**
   * 记录 LLM 响应
   */
  recordLLMResponse(params: {
    iteration: number;
    content: string;
    finishReason: string;
    tokenUsage: { input: number; output: number; total: number };
  }): void {
    this.writeEvent({
      event: 'llm_response',
      sessionId: this.sessionId!,
      iteration: params.iteration,
      contentPreview: params.content.slice(0, 200),
      finishReason: params.finishReason,
      tokenUsage: params.tokenUsage,
    });
  }

  /**
   * 记录工具调用
   */
  recordToolCall(params: {
    iteration: number;
    toolName: string;
    args: Record<string, unknown>;
  }): void {
    this.writeEvent({
      event: 'tool_call',
      sessionId: this.sessionId!,
      iteration: params.iteration,
      tool: params.toolName,
      args: params.args,
    });
  }

  /**
   * 记录工具结果
   */
  recordToolResult(params: {
    iteration: number;
    toolName: string;
    success: boolean;
    durationMs: number;
    resultPreview?: string;
    error?: string;
  }): void {
    this.writeEvent({
      event: 'tool_result',
      sessionId: this.sessionId!,
      iteration: params.iteration,
      tool: params.toolName,
      success: params.success,
      durationMs: params.durationMs,
      resultPreview: params.resultPreview?.slice(0, 500),
      error: params.error,
    });
  }

  /**
   * 记录迭代结束
   */
  recordIterationEnd(params: {
    iteration: number;
    durationMs: number;
  }): void {
    this.writeEvent({
      event: 'iteration_end',
      sessionId: this.sessionId!,
      iteration: params.iteration,
      durationMs: params.durationMs,
    });
  }

  /**
   * 获取录制文件路径
   */
  getFilePath(): string | null {
    return this.filePath;
  }

  // ===== 私有方法 =====

  /**
   * 写入事件到 JSONL 文件
   */
  private writeEvent(data: Record<string, unknown>): void {
    if (!this.filePath) return;

    const event: TraceEvent = {
      seq: this.seq++,
      ts: new Date().toISOString(),
      sessionId: this.sessionId!,
      ...data,
    } as TraceEvent;

    try {
      appendFileSync(this.filePath, JSON.stringify(event) + '\n', 'utf-8');
    } catch (error) {
      log.error('Trace event write failed:', error);
    }
  }
}
