import { Injectable, Logger } from '@nestjs/common';
import { EngineStreamEvent } from '../../../domain/ports/outbound/agent-engine.port';

/**
 * Parses Claude CLI stream-json output lines into EngineStreamEvent objects.
 * Each line from the CLI stdout (when using --output-format stream-json)
 * is a JSON object representing a streaming message event.
 */
@Injectable()
export class StreamJsonParser {
  private readonly logger = new Logger(StreamJsonParser.name);

  /**
   * Parses a single JSON line from the Claude CLI stream output.
   * Returns an array of EngineStreamEvents (may be zero or more per line).
   */
  parseLine(line: string): EngineStreamEvent[] {
    const trimmed = line.trim();
    if (!trimmed) return [];

    try {
      const parsed = JSON.parse(trimmed);
      return this.mapJsonToEvents(parsed);
    } catch {
      this.logger.debug(`Non-JSON line from CLI: ${trimmed}`);
      return [];
    }
  }

  /**
   * Maps a parsed JSON object from Claude CLI stream-json output
   * to an array of EngineStreamEvent objects.
   */
  private mapJsonToEvents(parsed: any): EngineStreamEvent[] {
    const events: EngineStreamEvent[] = [];

    if (parsed.type === 'assistant') {
      // Assistant messages contain content blocks
      if (Array.isArray(parsed.message?.content)) {
        for (const block of parsed.message.content) {
          const event = this.mapContentBlock(block);
          if (event) events.push(event);
        }
      } else if (parsed.content_block) {
        // Single content block variant
        const event = this.mapContentBlock(parsed.content_block);
        if (event) events.push(event);
      }
    } else if (parsed.type === 'tool_result') {
      events.push({
        type: 'tool_result',
        toolName: parsed.tool_name ?? parsed.name ?? 'unknown',
        output:
          typeof parsed.content === 'string'
            ? parsed.content
            : JSON.stringify(parsed.content ?? ''),
        isError: parsed.is_error ?? false,
      });
    } else if (parsed.type === 'result') {
      events.push({
        type: 'completed',
        summary: parsed.result ?? parsed.text ?? 'Task completed',
        tokensUsed:
          parsed.total_cost_usd !== undefined
            ? undefined
            : (parsed.usage?.input_tokens ?? 0) +
                (parsed.usage?.output_tokens ?? 0) || undefined,
      });
    } else if (parsed.type === 'error') {
      events.push({
        type: 'error',
        message:
          parsed.error?.message ?? parsed.message ?? 'Unknown CLI error',
        code: parsed.error?.code ?? 'CLI_ERROR',
      });
    }

    return events;
  }

  /**
   * Maps a single content block to an EngineStreamEvent.
   */
  private mapContentBlock(block: any): EngineStreamEvent | null {
    if (block.type === 'text' && block.text) {
      return { type: 'text', content: block.text };
    }
    if (block.type === 'tool_use') {
      return {
        type: 'tool_use',
        toolName: block.name,
        input: block.input ?? {},
      };
    }
    if (block.type === 'thinking' && block.thinking) {
      return { type: 'thinking', content: block.thinking };
    }
    return null;
  }
}
