"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.fetchNodeHttp = fetchNodeHttp;
const tslib_1 = require("tslib");
const node_http_1 = require("node:http");
const node_https_1 = require("node:https");
const node_stream_1 = require("node:stream");
const node_zlib_1 = tslib_1.__importDefault(require("node:zlib"));
const promise_helpers_1 = require("@whatwg-node/promise-helpers");
const Request_js_1 = require("./Request.js");
const Response_js_1 = require("./Response.js");
const URL_js_1 = require("./URL.js");
const utils_js_1 = require("./utils.js");
function getRequestFnForProtocol(url) {
    if (url.startsWith('http:')) {
        return node_http_1.request;
    }
    else if (url.startsWith('https:')) {
        return node_https_1.request;
    }
    throw new Error(`Unsupported protocol: ${url.split(':')[0] || url}`);
}
function fetchNodeHttp(fetchRequest) {
    return new Promise((resolve, reject) => {
        try {
            const requestFn = getRequestFnForProtocol(fetchRequest.parsedUrl?.protocol || fetchRequest.url);
            const headersSerializer = fetchRequest.headersSerializer || utils_js_1.getHeadersObj;
            const nodeHeaders = headersSerializer(fetchRequest.headers);
            nodeHeaders['accept-encoding'] ||= utils_js_1.DEFAULT_ACCEPT_ENCODING;
            if (nodeHeaders['user-agent'] == null && nodeHeaders['User-Agent'] == null) {
                nodeHeaders['user-agent'] = 'node';
            }
            let signal;
            if (fetchRequest._signal == null) {
                signal = undefined;
            }
            else if (fetchRequest._signal) {
                signal = fetchRequest._signal;
            }
            let nodeRequest;
            // If it is our ponyfilled Request, it should have `parsedUrl` which is a `URL` object
            if (fetchRequest.parsedUrl) {
                nodeRequest = requestFn(fetchRequest.parsedUrl, {
                    method: fetchRequest.method,
                    headers: nodeHeaders,
                    signal,
                    agent: fetchRequest.agent,
                });
            }
            else {
                nodeRequest = requestFn(fetchRequest.url, {
                    method: fetchRequest.method,
                    headers: nodeHeaders,
                    signal,
                    agent: fetchRequest.agent,
                });
            }
            nodeRequest.once('error', reject);
            nodeRequest.once('response', nodeResponse => {
                let outputStream;
                const contentEncoding = nodeResponse.headers['content-encoding'];
                switch (contentEncoding) {
                    case 'x-gzip':
                    case 'gzip':
                        outputStream = node_zlib_1.default.createGunzip();
                        break;
                    case 'x-deflate':
                    case 'deflate':
                        outputStream = node_zlib_1.default.createInflate();
                        break;
                    case 'x-deflate-raw':
                    case 'deflate-raw':
                        outputStream = node_zlib_1.default.createInflateRaw();
                        break;
                    case 'br':
                        outputStream = node_zlib_1.default.createBrotliDecompress();
                        break;
                    case 'zstd':
                        if (node_zlib_1.default.createZstdDecompress != null) {
                            outputStream = node_zlib_1.default.createZstdDecompress();
                        }
                        break;
                }
                if (nodeResponse.headers.location && (0, utils_js_1.shouldRedirect)(nodeResponse.statusCode)) {
                    if (fetchRequest.redirect === 'error') {
                        const redirectError = new Error('Redirects are not allowed');
                        reject(redirectError);
                        nodeResponse.resume();
                        return;
                    }
                    if (fetchRequest.redirect === 'follow') {
                        const redirectedUrl = new URL_js_1.PonyfillURL(nodeResponse.headers.location, fetchRequest.parsedUrl || fetchRequest.url);
                        const redirectResponse$ = fetchNodeHttp(new Request_js_1.PonyfillRequest(redirectedUrl, fetchRequest));
                        resolve(redirectResponse$.then(redirectResponse => {
                            redirectResponse.redirected = true;
                            return redirectResponse;
                        }));
                        nodeResponse.resume();
                        return;
                    }
                }
                outputStream ||= new node_stream_1.PassThrough();
                (0, utils_js_1.pipeThrough)({
                    src: nodeResponse,
                    dest: outputStream,
                    signal,
                    onError: e => {
                        if (!nodeResponse.destroyed) {
                            nodeResponse.destroy(e);
                        }
                        if (!outputStream.destroyed) {
                            outputStream.destroy(e);
                        }
                        reject(e);
                    },
                });
                const statusCode = nodeResponse.statusCode || 200;
                let statusText = nodeResponse.statusMessage || node_http_1.STATUS_CODES[statusCode];
                if (statusText == null) {
                    statusText = '';
                }
                const ponyfillResponse = new Response_js_1.PonyfillResponse(outputStream || nodeResponse, {
                    status: statusCode,
                    statusText,
                    headers: nodeResponse.headers,
                    url: fetchRequest.url,
                    signal,
                });
                resolve(ponyfillResponse);
            });
            if (fetchRequest['_buffer'] != null) {
                (0, promise_helpers_1.handleMaybePromise)(() => (0, utils_js_1.safeWrite)(fetchRequest['_buffer'], nodeRequest), () => (0, utils_js_1.endStream)(nodeRequest), reject);
            }
            else if (fetchRequest['bodyType'] === 'String') {
                (0, promise_helpers_1.handleMaybePromise)(() => (0, utils_js_1.safeWrite)(fetchRequest['bodyInit'], nodeRequest), () => (0, utils_js_1.endStream)(nodeRequest), reject);
            }
            else {
                const nodeReadable = (fetchRequest.body != null
                    ? (0, utils_js_1.isNodeReadable)(fetchRequest.body)
                        ? fetchRequest.body
                        : node_stream_1.Readable.from(fetchRequest.body)
                    : null);
                if (nodeReadable) {
                    nodeReadable.pipe(nodeRequest);
                }
                else {
                    (0, utils_js_1.endStream)(nodeRequest);
                }
            }
        }
        catch (e) {
            reject(e);
        }
    });
}
