import { assertOkOrThrowProviderError } from "./provider-http-errors.js";
import { assertSafeUrl } from "./ssrf-guard.js";
import { setTimeout as setTimeout$1 } from "node:timers/promises";
//#region src/media-shared/http/provider-http.ts
/**
* Shared provider HTTP client (voice, STT/TTS, image-generation extensions).
*
* Wraps `globalThis.fetch` with SSRF guard (`assertSafeUrl`), timeout + signal
* merge, and standardized errors via `assertOkOrThrowProviderError`.
*/
function mergeHeadersRecord(input) {
	const out = {};
	if (!input) return out;
	if (input instanceof Headers) {
		input.forEach((value, key) => {
			out[key.toLowerCase()] = value;
		});
		return out;
	}
	for (const [k, v] of Object.entries(input)) if (typeof v === "string") out[k.toLowerCase()] = v;
	return out;
}
/**
* fetch + SSRF guard + timeout. Returns the raw Response (caller decides whether
* to assertOk / parse / stream). Throws SsrfBlockedError on guard failure.
*/
async function fetchWithTimeoutGuarded(url, options) {
	await assertSafeUrl(url, options);
	const controller = new AbortController();
	const timeoutHandle = setTimeout(() => controller.abort(), options.timeoutMs);
	const callerSignal = options.signal;
	if (callerSignal) if (callerSignal.aborted) controller.abort(callerSignal.reason);
	else callerSignal.addEventListener("abort", () => controller.abort(callerSignal.reason), { once: true });
	try {
		return await fetch(url, {
			...options.init,
			signal: controller.signal
		});
	} finally {
		clearTimeout(timeoutHandle);
	}
}
async function postJsonRequest(url, options) {
	const headers = mergeHeadersRecord(options.headers);
	if (!headers["content-type"]) headers["content-type"] = "application/json";
	if (!headers.accept) headers.accept = "application/json";
	const response = await fetchWithTimeoutGuarded(url, {
		...options,
		init: {
			method: "POST",
			headers,
			body: JSON.stringify(options.body)
		}
	});
	await assertOkOrThrowProviderError(response, options.label);
	return response;
}
async function postMultipartRequest(url, options) {
	const headers = mergeHeadersRecord(options.headers);
	const response = await fetchWithTimeoutGuarded(url, {
		...options,
		init: {
			method: "POST",
			headers,
			body: options.body
		}
	});
	await assertOkOrThrowProviderError(response, options.label);
	return response;
}
async function getJsonRequest(url, options) {
	const headers = mergeHeadersRecord(options.headers);
	if (!headers.accept) headers.accept = "application/json";
	const response = await fetchWithTimeoutGuarded(url, {
		...options,
		init: {
			method: "GET",
			headers
		}
	});
	await assertOkOrThrowProviderError(response, options.label);
	return response;
}
/**
* Normalize a base URL: trim trailing slash, force http(s) scheme.
*/
function normalizeBaseUrl(baseUrl) {
	const trimmed = baseUrl.trim().replace(/\/+$/, "");
	const parsed = new URL(trimmed);
	if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new Error(`Provider base URL must be http(s): "${baseUrl}"`);
	return trimmed;
}
function createProviderOperationDeadline(timeoutMs) {
	const deadlineMs = Date.now() + timeoutMs;
	return {
		deadlineMs,
		assertNotExpired() {
			if (Date.now() > deadlineMs) throw new Error(`Provider operation exceeded ${timeoutMs}ms timeout`);
		},
		remainingMs() {
			return Math.max(0, deadlineMs - Date.now());
		}
	};
}
async function waitProviderOperationPollInterval(intervalMs, signal) {
	if (intervalMs <= 0) return;
	await setTimeout$1(intervalMs, void 0, { signal });
}
//#endregion
export { createProviderOperationDeadline, fetchWithTimeoutGuarded, getJsonRequest, normalizeBaseUrl, postJsonRequest, postMultipartRequest, waitProviderOperationPollInterval };

//# sourceMappingURL=provider-http.js.map