import http from "http";
import https from "https";
import { URL } from "url";
import querystring from "querystring";
import zlib from "zlib";
import CookieManager from './cookieManager.js';
export const cookieManager = new CookieManager();
class fetch {
    constructor() { }
    decompress(data, encoding) {
        return encoding === "br" ? zlib.brotliDecompressSync(data) :
            encoding === "gzip" ? zlib.gunzipSync(data) :
                encoding === "deflate" ? zlib.inflateSync(data) :
                    data;
    }
    readData(res, mode) {
        return new Promise((resolve, reject) => {
            const chunks = [];
            res
                .on("data", chunk => chunks.push(chunk))
                .on("end", () => {
                try {
                    const data = this.decompress(Buffer.concat(chunks), res.headers["content-encoding"]);
                    resolve(mode === "json" ? JSON.parse(data.toString("utf8")) :
                        mode === "buffer" ? data :
                            mode === "arraybuffer" ? new Uint8Array(data).buffer :
                                data.toString("utf8"));
                }
                catch (err) {
                    reject(err);
                }
            })
                .on("error", reject);
        });
    }
    async fetch(urlString, options = {}, redirectCount = 0) {
        options.followRedirects = options.followRedirects ?? true;
        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) } : {}),
                "Cookie": cookieManager.format(hostname),
                "Accept-Encoding": "br, gzip, deflate",
            }
        };
        return new Promise((resolve, reject) => {
            const requester = protocol === "https:" ? https : http;
            const req = requester.request(requestOptions, res => {
                cookieManager.parse(res.headers["set-cookie"], hostname);
                if (options.followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
                    req.destroy();
                    if (redirectCount >= (options.maxRedirects || 5))
                        return reject(new Error("Max redirects exceeded"));
                    if (!res.headers.location)
                        return reject(new Error("Redirect location not provided"));
                    const nextUrl = new URL(res.headers.location, urlString);
                    return resolve(this.fetch(nextUrl.toString(), options, redirectCount + 1));
                }
                return resolve({
                    body: res,
                    headers: res.headers,
                    cookies: cookieManager.getCookies(hostname),
                    text: () => this.readData(res, "text"),
                    json: () => this.readData(res, "json"),
                    buffer: () => this.readData(res, "buffer"),
                    arrayBuffer: () => this.readData(res, "arraybuffer"),
                });
            });
            req.setTimeout(options.timeout || 5e3, () => {
                req.destroy();
                reject(new Error("Request timed out"));
            });
            req.on("error", reject);
            if (options.signal) {
                options.signal.addEventListener("abort", () => {
                    req.destroy(new Error("Request aborted"));
                    reject(new Error("Request aborted"));
                });
            }
            if (body)
                req.write(body);
            req.end();
        });
    }
}
const inst = new fetch();
export default inst.fetch.bind(inst);