import { PassThrough } from 'node:stream';
import { DEFAULT_ANTHROPIC_CONVERSION_CONFIG } from '../types/index.js';
import { ErrorUtils } from '../shared/utils.js';
import { createAnthropicSequencer } from './sequencers/anthropic-sequencer.js';
import { createAnthropicStreamWriter } from '../shared/writer.js';
export class AnthropicJsonToSseConverter {
    config = DEFAULT_ANTHROPIC_CONVERSION_CONFIG;
    contexts = new Map();
    constructor(config) {
        if (config) {
            this.config = { ...this.config, ...config };
        }
    }
    async convertResponseToJsonToSse(response, options) {
        const context = this.createContext(response, options);
        this.contexts.set(options.requestId, context);
        const stream = new PassThrough({ objectMode: true });
        const writer = createAnthropicStreamWriter(stream, {
            onEvent: (event) => this.updateStats(context, event),
            onError: (error) => this.handleStreamError(context, error, stream)
        });
        this.processResponse(response, context, writer, stream).catch((error) => {
            this.handleStreamError(context, error, stream);
        });
        return Object.assign(stream, {
            protocol: 'anthropic-messages',
            direction: 'json_to_sse',
            requestId: options.requestId,
            getStats: () => context.eventStats,
            complete: () => writer.complete(),
            abort: (error) => writer.abort(error)
        });
    }
    createContext(response, options) {
        const stats = {
            totalEvents: 0,
            contentBlocks: 0,
            toolUseBlocks: 0,
            thinkingBlocks: 0,
            textBlocks: 0,
            errors: 0,
            startTime: Date.now()
        };
        return {
            requestId: options.requestId,
            model: options.model,
            response,
            options,
            startTime: Date.now(),
            eventStats: stats
        };
    }
    async processResponse(response, context, writer, stream) {
        try {
            this.validateResponse(response);
            const sequencer = createAnthropicSequencer({
                chunkSize: context.options.chunkSize ?? this.config.defaultChunkSize,
                chunkDelayMs: context.options.chunkDelayMs ?? this.config.defaultDelayMs,
                enableDelay: Boolean(context.options.chunkDelayMs),
                reasoningMode: context.options.reasoningMode ?? this.config.reasoningMode,
                reasoningTextPrefix: context.options.reasoningTextPrefix ?? this.config.reasoningTextPrefix
            });
            const events = sequencer.sequenceResponse(response, context.requestId);
            await writer.writeAnthropicEvents(events);
            writer.complete();
        }
        catch (error) {
            writer.abort(error);
            throw this.wrapError('ANTHROPIC_JSON_TO_SSE_FAILED', error, context.requestId);
        }
        finally {
            this.contexts.delete(context.requestId);
            stream.end();
        }
    }
    updateStats(context, event) {
        context.eventStats.totalEvents += 1;
        if (!event || typeof event !== 'object')
            return;
        const type = event.type;
        if (type === 'content_block_start') {
            context.eventStats.contentBlocks += 1;
            const blockType = event.data?.content_block?.type;
            if (blockType === 'tool_use')
                context.eventStats.toolUseBlocks += 1;
            if (blockType === 'thinking')
                context.eventStats.thinkingBlocks += 1;
            if (blockType === 'text')
                context.eventStats.textBlocks += 1;
        }
    }
    validateResponse(response) {
        if (!response || typeof response !== 'object') {
            throw new Error('Invalid Anthropic response payload');
        }
        if (!Array.isArray(response.content)) {
            throw new Error('Anthropic response content must be an array');
        }
    }
    handleStreamError(context, error, stream) {
        context.eventStats.errors += 1;
        try {
            stream.destroy(error);
        }
        catch {
            /* noop */
        }
    }
    wrapError(code, error, requestId) {
        return ErrorUtils.createError(error.message, code, { requestId });
    }
}
