import { EventEmitter } from 'events';
import { Headers } from '../../../lib/headers/Headers.js';
import { StatusParser } from './StatusParser.js';
import { HeadersParser } from './HeadersParser.js';
import { BodyParser } from './BodyParser.js';
export var RequestState;
(function (RequestState) {
    RequestState[RequestState["Status"] = 0] = "Status";
    RequestState[RequestState["Headers"] = 1] = "Headers";
    RequestState[RequestState["Body"] = 2] = "Body";
    RequestState[RequestState["Done"] = 3] = "Done";
})(RequestState || (RequestState = {}));
/**
 * HTTP Response Parser
 * Handles parsing of HTTP response data including status, headers, and body
 */
export class HttpResponseParser extends EventEmitter {
    state = RequestState.Status;
    rawHeaders;
    responseInfo;
    logger;
    callbacks;
    aborted = false;
    statusParser;
    headersParser;
    bodyParser;
    constructor(logger, callbacks) {
        super();
        this.logger = logger;
        this.callbacks = callbacks;
        this.responseInfo = {
            chunked: false,
        };
        this.statusParser = new StatusParser(logger);
        this.headersParser = new HeadersParser(logger);
        this.bodyParser = new BodyParser(logger, this.responseInfo);
    }
    /**
     * Process incoming HTTP response data
     * @param data Buffer containing HTTP response data
     */
    processData(data) {
        if (this.aborted) {
            return;
        }
        let remaining = data;
        if (this.state === RequestState.Status) {
            remaining = this.processStatus(remaining);
            if (!remaining) {
                return;
            }
        }
        if (this.state === RequestState.Headers) {
            remaining = this.processHeaders(remaining);
            if (!remaining) {
                return;
            }
        }
        if (this.state === RequestState.Body) {
            this.processBody(remaining);
        }
    }
    /**
     * Process HTTP status line
     * @param data Buffer containing status line data
     * @returns Remaining data after status parsing
     */
    processStatus(data) {
        if (this.aborted) {
            return;
        }
        if (!data) {
            return;
        }
        try {
            const result = this.statusParser.parse(data);
            if (result) {
                this.responseInfo.status = result.status;
                this.callbacks.onStatusParsed(result.status, result.statusText);
                this.state = RequestState.Headers;
                return result.remaining;
            }
        }
        catch (error) {
            this.callbacks.onError(error);
        }
        return data;
    }
    /**
     * Process HTTP headers
     * @param data Buffer containing header data
     * @returns Remaining data after header parsing
     */
    processHeaders(data) {
        if (this.aborted) {
            return;
        }
        if (!data) {
            this.parseHeaders();
            return;
        }
        try {
            const result = this.headersParser.parse(data, this.rawHeaders);
            if (result.complete) {
                this.rawHeaders = result.headers;
                this.parseHeaders(this.rawHeaders);
                delete this.rawHeaders;
                this.state = RequestState.Body;
                return this.postHeaders(result.remaining);
            }
            else {
                this.rawHeaders = result.headers;
                return;
            }
        }
        catch (error) {
            this.callbacks.onError(error);
        }
        return data;
    }
    /**
     * Parse headers and extract response information
     * @param buffer Raw headers buffer
     */
    parseHeaders(buffer) {
        let raw = '';
        if (buffer) {
            raw = buffer.toString();
        }
        this.logger.info('Received headers list', raw);
        const headers = new Headers(raw);
        // Extract response information
        if (headers.has('Content-Length')) {
            this.responseInfo.contentLength = Number(headers.get('Content-Length'));
        }
        if (headers.has('Transfer-Encoding')) {
            const tr = headers.get('Transfer-Encoding');
            if (tr === 'chunked') {
                this.responseInfo.chunked = true;
            }
        }
        this.callbacks.onHeadersParsed(headers);
        const rawHeaders = headers.toString();
        const detail = {
            returnValue: true,
            value: rawHeaders,
        };
        this.callbacks.emit('headersreceived', detail);
        if (!detail.returnValue) {
            this.abort();
        }
    }
    /**
     * Check if response should end after headers
     * @param data Remaining data after headers
     * @returns Data to continue processing or undefined to end
     */
    postHeaders(data) {
        // Check if response status indicates no body should be present
        const status = this.responseInfo.status;
        if (typeof status === 'number' && (status === 204 || status === 304 || (status >= 100 && status < 200))) {
            // 204 No Content, 304 Not Modified, and informational responses do not have a body
            this.state = RequestState.Done;
            this.callbacks.onBodyComplete(Buffer.alloc(0));
            return;
        }
        if (!data || data.length === 0) {
            if (this.responseInfo.contentLength === 0) {
                this.state = RequestState.Done;
                this.callbacks.onBodyComplete(Buffer.alloc(0));
                return;
            }
            return;
        }
        return data;
    }
    /**
     * Process HTTP body data
     * @param data Buffer containing body data
     */
    processBody(data) {
        if (this.aborted || !data) {
            return;
        }
        try {
            if (this.responseInfo.chunked) {
                this.bodyParser.processChunked(data, (body) => {
                    this.state = RequestState.Done;
                    this.callbacks.onBodyComplete(body);
                });
            }
            else {
                this.bodyParser.processContentLength(data, (body) => {
                    this.state = RequestState.Done;
                    this.callbacks.onBodyComplete(body);
                });
            }
        }
        catch (error) {
            this.callbacks.onError(error);
        }
    }
    /**
     * Abort the parser
     */
    abort() {
        this.aborted = true;
        this.state = RequestState.Done;
        this.callbacks.onAbort();
    }
    /**
     * Reset parser state
     */
    reset() {
        this.state = RequestState.Status;
        this.rawHeaders = undefined;
        this.responseInfo = {
            chunked: false,
        };
        this.aborted = false;
        this.bodyParser = new BodyParser(this.logger, this.responseInfo);
    }
    /**
     * Get current parser state
     */
    getState() {
        return this.state;
    }
    /**
     * Get response information
     */
    getResponseInfo() {
        return this.responseInfo;
    }
}
//# sourceMappingURL=HttpResponseParser.js.map