import createClient from "openapi-fetch";
import { getLocalTransportClient } from "./local_transport.js";
/**
 * Build a `fetch` wrapper that attempts to sign outbound requests with the
 * CLI-side AAuth keypair. When signing is disabled or no keypair is
 * configured the returned function is the global `fetch` unchanged. This
 * indirection keeps Node-only `fs`/`jose` imports out of the browser
 * bundle via a dynamic import.
 */
function buildMaybeSignedFetch(enabled) {
    if (!enabled)
        return fetch;
    return (async (input, init) => {
        const request = input instanceof Request ? input : null;
        const url = request ? request.url : typeof input === "string" ? input : input.toString();
        const { cliSignedFetch } = await import("../cli/aauth_signer.js");
        const method = (init?.method ?? request?.method ?? "GET").toUpperCase();
        const headers = {};
        const sourceHeaders = new Headers(request?.headers);
        if (init?.headers) {
            const overrides = new Headers(init.headers);
            overrides.forEach((value, key) => {
                sourceHeaders.set(key, value);
            });
        }
        if (sourceHeaders) {
            const h = new Headers(sourceHeaders);
            h.forEach((value, key) => {
                headers[key] = value;
            });
        }
        let body = typeof init?.body === "string" ? init.body : undefined;
        if (body === undefined && request && method !== "GET" && method !== "HEAD") {
            body = await request.clone().text();
        }
        try {
            return await cliSignedFetch(url, {
                method,
                headers,
                body,
                signal: init?.signal ?? undefined,
            });
        }
        catch {
            // Never surface signing misconfiguration as a hard failure from the
            // API client — the caller should still reach the server and land
            // as `unverified_client` / `anonymous` tier.
            return fetch(input, init);
        }
    });
}
export function createApiClient(options = {}) {
    const headers = {};
    if (options.token) {
        headers.Authorization = `Bearer ${options.token}`;
    }
    const signingEnabled = options.signWithCliAAuth ??
        (process.env.NODE_ENV !== "test" &&
            process.env.NEOTOMA_CLI_AAUTH_DISABLE !== "1" &&
            process.env.NEOTOMA_CLI_AAUTH_ENABLE !== "0");
    const fetchImpl = buildMaybeSignedFetch(signingEnabled);
    const client = createClient({
        baseUrl: options.baseUrl,
        headers,
        fetch: fetchImpl,
    });
    const defaultFallback = process.env.NODE_ENV === "test"
        ? false
        : process.env.NEOTOMA_ENABLE_OFFLINE_FALLBACK === "true" &&
            process.env.NEOTOMA_DISABLE_OFFLINE_FALLBACK !== "true";
    const shouldFallback = options.useOfflineFallback ?? defaultFallback;
    const forceLocal = process.env.NEOTOMA_FORCE_LOCAL_TRANSPORT === "true";
    const canFallbackForPath = (path) => path !== "/health";
    const isNetworkError = (err) => {
        if (!(err instanceof Error))
            return false;
        const causeCode = err.cause?.code;
        const message = err.message ?? "";
        return (message === "fetch failed" ||
            message.includes("ECONNREFUSED") ||
            message.includes("ECONNRESET") ||
            message.includes("timeout") ||
            causeCode === "ECONNREFUSED" ||
            causeCode === "ECONNRESET" ||
            causeCode === "ETIMEDOUT");
    };
    const wrapMethod = (method) => {
        const original = client[method].bind(client);
        return (async (path, ...args) => {
            if (forceLocal && canFallbackForPath(path)) {
                const localClient = await getLocalTransportClient({
                    token: options.token,
                    baseUrl: options.baseUrl,
                });
                const localMethod = localClient[method].bind(localClient);
                return localMethod(path, ...args);
            }
            try {
                return await original(path, ...args);
            }
            catch (err) {
                if (!shouldFallback || !canFallbackForPath(path) || !isNetworkError(err)) {
                    throw err;
                }
                const localClient = await getLocalTransportClient({
                    token: options.token,
                    baseUrl: options.baseUrl,
                });
                const localMethod = localClient[method].bind(localClient);
                return localMethod(path, ...args);
            }
        });
    };
    return {
        ...client,
        GET: wrapMethod("GET"),
        POST: wrapMethod("POST"),
        PUT: wrapMethod("PUT"),
        DELETE: wrapMethod("DELETE"),
    };
}
//# sourceMappingURL=api_client.js.map