/**
 * Http Client - Fluent HTTP request builder
 *
 * Usage:
 *   await HttpClient.get('https://api.example.com/users').withAuth(token).send();
 *   await HttpClient.post('https://api.example.com/users', data).withTimeout(5000).send();
 */
import { OpenTelemetry } from '../../observability/OpenTelemetry.js';
import { Env } from '../../config/env.js';
import { Logger } from '../../config/logger.js';
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
import { createHttpResponse } from './HttpResponse.js';
/**
 * Perform the actual request for a given state. Separated to keep the builder small
 */
async function performRequest(state) {
    const { response, bodyText, durationMs } = await performRequestRaw(state);
    Logger.debug(`HTTP ${state.method} ${state.url}`, {
        status: response.status,
        duration: `${durationMs}ms`,
        size: bodyText.length,
    });
    return createHttpResponse(response, bodyText);
}
async function performRequestRaw(state) {
    const { response, durationMs } = await performFetch(state);
    const bodyText = await response.text();
    return { response, bodyText, durationMs };
}
async function performFetch(state) {
    const timeout = state.timeout ?? Env.getInt('HTTP_TIMEOUT', 30000);
    const controller = new AbortController();
    let timeoutId;
    if (timeout > 0) {
        timeoutId = globalThis.setTimeout(() => controller.abort(), timeout);
    }
    const buildInit = () => {
        if (OpenTelemetry.isEnabled()) {
            OpenTelemetry.injectTraceHeaders(state.headers);
        }
        const init = {
            method: state.method,
            headers: state.headers,
            signal: controller.signal,
        };
        if (state.body !== undefined &&
            state.body !== null &&
            ['POST', 'PUT', 'PATCH'].includes(state.method)) {
            init.body = state.body;
        }
        return init;
    };
    try {
        const init = buildInit();
        const startTime = Date.now();
        const response = await globalThis.fetch(state.url, init);
        const duration = Date.now() - startTime;
        return { response, durationMs: duration };
    }
    catch (error) {
        if (error instanceof Error && error.name === 'AbortError') {
            throw ErrorFactory.createConnectionError(`HTTP request timeout after ${timeout}ms`, {
                url: state.url,
                method: state.method,
                timeout,
            });
        }
        throw ErrorFactory.createTryCatchError(`HTTP request failed: ${error.message}`, {
            url: state.url,
            method: state.method,
            error: error instanceof Error ? error.message : String(error),
        });
    }
    finally {
        if (timeoutId !== undefined) {
            globalThis.clearTimeout(timeoutId);
        }
    }
}
/**
 * Create request builder with fluent API
 */
function createRequestBuilder(method, url, initialBody) {
    const state = {
        method,
        url,
        headers: {
            'User-Agent': 'ZinTrust/1.0',
        },
        body: initialBody ? JSON.stringify(initialBody) : undefined,
    };
    const self = {
        withHeader(name, value) {
            state.headers[name] = value;
            return self;
        },
        withHeaders(headers) {
            Object.assign(state.headers, headers);
            return self;
        },
        withAuth(token, scheme = 'Bearer') {
            state.headers['Authorization'] = `${scheme} ${token}`;
            return self;
        },
        withBasicAuth(username, password) {
            const credentials = Buffer.from(`${username}:${password}`).toString('base64');
            state.headers['Authorization'] = `Basic ${credentials}`;
            return self;
        },
        withTimeout(ms) {
            state.timeout = ms;
            return self;
        },
        asJson() {
            state.contentType = 'json';
            state.headers['Content-Type'] = 'application/json';
            return self;
        },
        asForm() {
            state.contentType = 'form';
            state.headers['Content-Type'] = 'application/x-www-form-urlencoded';
            return self;
        },
        async send() {
            return performRequest(state);
        },
        async sendRaw() {
            const { response } = await performFetch(state);
            return response;
        },
        async sendStream() {
            const { response } = await performFetch(state);
            return { response, stream: response.body };
        },
    };
    return self;
}
/**
 * HTTP Client - Sealed namespace for making HTTP requests
 */
export const HttpClient = Object.freeze({
    /**
     * Make GET request
     */
    get(url) {
        return createRequestBuilder('GET', url);
    },
    /**
     * Make POST request
     */
    post(url, data) {
        const builder = createRequestBuilder('POST', url, data);
        if (data) {
            builder.asJson();
        }
        return builder;
    },
    /**
     * Make PUT request
     */
    put(url, data) {
        const builder = createRequestBuilder('PUT', url, data);
        if (data) {
            builder.asJson();
        }
        return builder;
    },
    /**
     * Make PATCH request
     */
    patch(url, data) {
        const builder = createRequestBuilder('PATCH', url, data);
        if (data) {
            builder.asJson();
        }
        return builder;
    },
    /**
     * Make DELETE request
     */
    delete(url, data) {
        const builder = createRequestBuilder('DELETE', url, data);
        if (data) {
            builder.asJson();
        }
        return builder;
    },
});
export default HttpClient;
