/**
 * Chunk Handler
 *
 * Handles different types of stream chunks with type-safe processing
 * and content accumulation.
 */
import { EventEmitter } from 'events';
export class ChunkHandler extends EventEmitter {
    options;
    accumulated;
    chunkCount = 0;
    constructor(options = {}) {
        super();
        this.options = {
            maxContentLength: options.maxContentLength ?? 1000000, // 1MB default
            validateChunks: options.validateChunks ?? true,
            typeHandlers: options.typeHandlers,
        };
        this.accumulated = this.createEmptyAccumulator();
    }
    createEmptyAccumulator() {
        return {
            content: '',
            chunks: [],
            toolCalls: [],
            toolResults: [],
            tokenCount: 0,
            errors: [],
        };
    }
    /**
     * Handle a single chunk
     */
    handle(chunk) {
        // Validate if enabled
        if (this.options.validateChunks) {
            const validation = this.validateChunk(chunk);
            if (!validation.valid) {
                this.emit('validationError', { chunk, errors: validation.errors });
                return;
            }
        }
        this.chunkCount++;
        this.accumulated.chunks.push(chunk);
        // Process by type
        switch (chunk.type) {
            case 'content':
                this.handleContentChunk(chunk);
                break;
            case 'tool_call':
                this.handleToolCallChunk(chunk);
                break;
            case 'tool_result':
                this.handleToolResultChunk(chunk);
                break;
            case 'token_count':
                this.handleTokenCountChunk(chunk);
                break;
            case 'error':
                this.handleErrorChunk(chunk);
                break;
            case 'done':
                this.handleDoneChunk(chunk);
                break;
        }
        // Call custom handler if defined
        const customHandler = this.options.typeHandlers?.[chunk.type];
        if (customHandler) {
            customHandler(chunk);
        }
        this.emit('chunk', { chunk, accumulated: this.getAccumulated() });
    }
    /**
     * Handle multiple chunks
     */
    handleMany(chunks) {
        for (const chunk of chunks) {
            this.handle(chunk);
        }
    }
    /**
     * Process an async iterable of chunks
     */
    async *process(source) {
        for await (const chunk of source) {
            this.handle(chunk);
            yield chunk;
        }
    }
    handleContentChunk(chunk) {
        if (chunk.content) {
            // Check max length
            if (this.accumulated.content.length + chunk.content.length > this.options.maxContentLength) {
                this.emit('contentOverflow', {
                    currentLength: this.accumulated.content.length,
                    chunkLength: chunk.content.length,
                    maxLength: this.options.maxContentLength,
                });
                // Truncate to fit
                const remaining = this.options.maxContentLength - this.accumulated.content.length;
                if (remaining > 0) {
                    this.accumulated.content += chunk.content.slice(0, remaining);
                }
                return;
            }
            this.accumulated.content += chunk.content;
        }
        this.emit('content', { content: chunk.content, total: this.accumulated.content });
    }
    handleToolCallChunk(chunk) {
        if (chunk.toolCall) {
            this.accumulated.toolCalls.push(chunk.toolCall);
            this.emit('toolCall', { toolCall: chunk.toolCall });
        }
    }
    handleToolResultChunk(chunk) {
        if (chunk.toolResult) {
            this.accumulated.toolResults.push(chunk.toolResult);
            this.emit('toolResult', { toolResult: chunk.toolResult });
        }
    }
    handleTokenCountChunk(chunk) {
        if (chunk.tokenCount !== undefined) {
            this.accumulated.tokenCount = chunk.tokenCount;
            this.emit('tokenCount', { tokenCount: chunk.tokenCount });
        }
    }
    handleErrorChunk(chunk) {
        if (chunk.error) {
            this.accumulated.errors.push(chunk.error);
            this.emit('error', { error: chunk.error });
        }
    }
    handleDoneChunk(_chunk) {
        this.emit('done', { accumulated: this.getAccumulated() });
    }
    /**
     * Validate a chunk structure
     */
    validateChunk(chunk) {
        const errors = [];
        if (!chunk || typeof chunk !== 'object') {
            errors.push('Chunk must be an object');
            return { valid: false, errors };
        }
        if (!chunk.type) {
            errors.push('Chunk must have a type');
        }
        else if (!this.isValidChunkType(chunk.type)) {
            errors.push(`Invalid chunk type: ${chunk.type}`);
        }
        // Type-specific validation
        switch (chunk.type) {
            case 'content':
                if (chunk.content !== undefined && typeof chunk.content !== 'string') {
                    errors.push('Content chunk must have string content');
                }
                break;
            case 'tool_call':
                if (chunk.toolCall) {
                    if (!chunk.toolCall.id || typeof chunk.toolCall.id !== 'string') {
                        errors.push('Tool call must have string id');
                    }
                    if (!chunk.toolCall.name || typeof chunk.toolCall.name !== 'string') {
                        errors.push('Tool call must have string name');
                    }
                }
                break;
            case 'token_count':
                if (chunk.tokenCount !== undefined && typeof chunk.tokenCount !== 'number') {
                    errors.push('Token count must be a number');
                }
                break;
            case 'error':
                if (chunk.error !== undefined && typeof chunk.error !== 'string') {
                    errors.push('Error must be a string');
                }
                break;
        }
        return { valid: errors.length === 0, errors };
    }
    isValidChunkType(type) {
        return ['content', 'tool_call', 'tool_result', 'token_count', 'error', 'done'].includes(type);
    }
    /**
     * Get accumulated content
     */
    getAccumulated() {
        return { ...this.accumulated };
    }
    /**
     * Get content string
     */
    getContent() {
        return this.accumulated.content;
    }
    /**
     * Get chunk count
     */
    getChunkCount() {
        return this.chunkCount;
    }
    /**
     * Check if stream has errors
     */
    hasErrors() {
        return this.accumulated.errors.length > 0;
    }
    /**
     * Check if stream has tool calls
     */
    hasToolCalls() {
        return this.accumulated.toolCalls.length > 0;
    }
    /**
     * Reset handler state
     */
    reset() {
        this.accumulated = this.createEmptyAccumulator();
        this.chunkCount = 0;
    }
    /**
     * Create a content chunk
     */
    static createContentChunk(content) {
        return { type: 'content', content };
    }
    /**
     * Create a tool call chunk
     */
    static createToolCallChunk(id, name, args) {
        return {
            type: 'tool_call',
            toolCall: { id, name, arguments: args },
        };
    }
    /**
     * Create a tool result chunk
     */
    static createToolResultChunk(id, success, output, error) {
        return {
            type: 'tool_result',
            toolResult: { id, success, output, error },
        };
    }
    /**
     * Create a token count chunk
     */
    static createTokenCountChunk(count) {
        return { type: 'token_count', tokenCount: count };
    }
    /**
     * Create an error chunk
     */
    static createErrorChunk(error) {
        return { type: 'error', error };
    }
    /**
     * Create a done chunk
     */
    static createDoneChunk() {
        return { type: 'done' };
    }
}
//# sourceMappingURL=chunk-handler.js.map