import http from "node:http";
import https from "node:https";
import { Readable } from "node:stream";
import { readDnsPinnedAddresses } from "./fetch-capabilities.js";
// DNS pinning must connect directly; an environment proxy would resolve the hostname itself.
const directHttpAgent = new http.Agent({ proxyEnv: {} });
const directHttpsAgent = new https.Agent({ proxyEnv: {} });
function getInputUrl(input) {
    if (typeof input === "string")
        return input;
    if (input instanceof URL)
        return input.href;
    return input.url;
}
function createPinnedLookup(addresses) {
    const pinnedAddresses = addresses.map((entry) => ({
        address: entry.address,
        family: entry.family ?? 4,
    }));
    return (_hostname, options, callback) => {
        if (options?.all) {
            callback(null, pinnedAddresses);
            return;
        }
        const first = pinnedAddresses[0];
        callback(null, first?.address ?? "0.0.0.0", first?.family ?? 4);
    };
}
function headersFrom(input, init) {
    if (init?.headers)
        return new Headers(init.headers);
    if (typeof input !== "string" && !(input instanceof URL))
        return new Headers(input.headers);
    return new Headers();
}
function methodFrom(input, init) {
    if (init?.method)
        return init.method;
    if (typeof input !== "string" && !(input instanceof URL))
        return input.method;
    return "GET";
}
function hasRequestBody(input, init) {
    if (init && "body" in init && init.body != null)
        return true;
    if (typeof input !== "string" && !(input instanceof URL))
        return input.body != null;
    return false;
}
export async function fetchWithDnsPinnedAddresses(input, init) {
    const addresses = readDnsPinnedAddresses(init);
    if (!addresses)
        throw new Error("Pinned DNS fetch missing validated addresses");
    if (hasRequestBody(input, init)) {
        throw new Error("Pinned DNS fetch does not support request bodies");
    }
    const url = new URL(getInputUrl(input));
    const client = url.protocol === "https:" ? https : http;
    const headers = {};
    headersFrom(input, init).forEach((value, key) => {
        headers[key] = value;
    });
    return await new Promise((resolve, reject) => {
        const req = client.request(url, {
            agent: url.protocol === "https:" ? directHttpsAgent : directHttpAgent,
            headers,
            lookup: createPinnedLookup(addresses),
            method: methodFrom(input, init),
            ...(init?.signal ? { signal: init.signal } : {}),
        }, (res) => {
            const responseHeaders = new Headers();
            for (const [key, value] of Object.entries(res.headers)) {
                if (Array.isArray(value)) {
                    for (const entry of value)
                        responseHeaders.append(key, entry);
                }
                else if (typeof value === "string") {
                    responseHeaders.set(key, value);
                }
            }
            const response = new Response(Readable.toWeb(res), {
                headers: responseHeaders,
                status: res.statusCode ?? 200,
                statusText: res.statusMessage,
            });
            Object.defineProperty(response, "url", { configurable: true, value: url.href });
            resolve(response);
        });
        req.on("error", reject);
        req.end();
    });
}
//# sourceMappingURL=dns-pinned-fetch.js.map