import http from "http";
import https from "https";
import { URL } from "url";
import querystring from "querystring";
import zlib from "zlib";
const decompress = (data, encoding) => {
    switch (encoding) {
        case "br": return zlib.brotliDecompressSync(data);
        case "gzip": return zlib.gunzipSync(data);
        case "deflate": return zlib.inflateSync(data);
    }
    return data;
};
const readData = (res, mode) => new Promise((resolve, reject) => {
    const chunks = [];
    res
        .on("data", chunk => chunks.push(chunk))
        .on("end", () => {
        try {
            const data = decompress(Buffer.concat(chunks), res.headers["content-encoding"]);
            if (mode === "json")
                resolve(JSON.parse(data.toString("utf8")));
            else if (mode === "buffer")
                resolve(data);
            else if (mode === "arraybuffer")
                resolve(new Uint8Array(data).buffer);
            else
                resolve(data.toString("utf8"));
        }
        catch (err) {
            reject(err);
        }
    })
        .on("error", reject);
});
export default async (urlString, options = {}) => {
    const { protocol, hostname, pathname, search, port } = new URL(urlString);
    const body = options.method === "POST" && options.body
        ? options.headers?.["Content-Type"] === "application/json"
            ? JSON.stringify(options.body)
            : querystring.stringify(options.body)
        : null;
    const requestOptions = {
        hostname,
        port: port || (protocol === "https:" ? 443 : 80),
        path: pathname + search,
        method: options.method || "GET",
        headers: {
            ...options.headers,
            ...(body ? { "Content-Length": Buffer.byteLength(body) } : {}),
            "Accept-Encoding": "br, gzip, deflate"
        }
    };
    return new Promise((resolve, reject) => {
        const requester = protocol === "https:" ? https : http;
        const req = requester.request(requestOptions, res => {
            if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300))
                return reject(new Error(`Request failed with status code ${res.statusCode}`));
            resolve({
                body: res,
                headers: res.headers,
                text: () => readData(res, "text"),
                json: () => readData(res, "json"),
                buffer: () => readData(res, "buffer"),
                arrayBuffer: () => readData(res, "arraybuffer")
            });
        });
        req.setTimeout(options.timeout || 5e3, () => {
            req.destroy();
            reject(new Error("Request timed out"));
        });
        req.on("error", reject);
        if (body)
            req.write(body);
        req.end();
    });
};