import { encode, fromUint8Array } from "js-base64";
import { shouldUseProxy } from "@scalar/helpers/url/redirect-to-proxy";
import { replaceVariables } from "@scalar/helpers/regex/replace-variables";
import { isRelativePath } from "@scalar/helpers/url/is-relative-path";
import { makeUrlAbsolute } from "@scalar/helpers/url/make-url-absolute";
//#region src/views/Request/libs/oauth2.ts
/**
* Interpolates server URL template variables (e.g. `https://{env}.example.com`)
* using the user-set `value` when available, falling back to the OpenAPI `default`.
* This is needed so OAuth flows that use the server URL as a base resolve correctly
* when the spec defines server variables.
*/
var getInterpolatedServerUrl = (activeServer) => {
	if (!activeServer?.url) return void 0;
	const serverVariables = Object.entries(activeServer.variables ?? {}).reduce((acc, [name, variable]) => {
		const value = variable.value || variable.default;
		if (value) acc[name] = value;
		return acc;
	}, {});
	return replaceVariables(activeServer.url, serverVariables);
};
var getActiveServerBase = (activeServer) => {
	const serverUrl = getInterpolatedServerUrl(activeServer);
	if (!serverUrl) return {};
	if (isRelativePath(serverUrl)) return typeof window === "undefined" ? {} : { basePath: serverUrl };
	return { baseUrl: serverUrl };
};
/**
* Generates a random string for PKCE code verifier
*
* @see https://www.rfc-editor.org/rfc/rfc7636#page-8
*/
var generateCodeVerifier = () => {
	const buffer = new Uint8Array(32);
	crypto.getRandomValues(buffer);
	return fromUint8Array(buffer, true);
};
/**
* Creates a code challenge from the code verifier
*/
var generateCodeChallenge = async (verifier, encoding) => {
	if (encoding === "plain") return verifier;
	if (typeof crypto?.subtle?.digest !== "function") {
		console.warn("SHA-256 is only supported when using https, using a plain text code challenge instead.");
		return verifier;
	}
	const data = new TextEncoder().encode(verifier);
	const digest = await crypto.subtle.digest("SHA-256", data);
	return fromUint8Array(new Uint8Array(digest), true);
};
/**
* Authorize oauth2 flow
*
* @returns the accessToken
*/
var authorizeOauth2 = async (flow, activeServer, proxyUrl) => {
	try {
		if (!flow) return [/* @__PURE__ */ new Error("Flow not found"), null];
		const scopes = flow.selectedScopes.join(" ");
		if (flow.type === "clientCredentials" || flow.type === "password") return authorizeServers(flow, scopes, { proxyUrl }, activeServer);
		const state = (Math.random() + 1).toString(36).substring(2, 10);
		const authorizationUrl = makeUrlAbsolute(flow.authorizationUrl, getActiveServerBase(activeServer));
		const url = new URL(authorizationUrl);
		/** Special PKCE state */
		let pkce = null;
		if (flow.type === "implicit") url.searchParams.set("response_type", "token");
		else if (flow.type === "authorizationCode") {
			url.searchParams.set("response_type", "code");
			if (flow["x-usePkce"] !== "no") {
				const codeVerifier = generateCodeVerifier();
				const codeChallenge = await generateCodeChallenge(codeVerifier, flow["x-usePkce"]);
				pkce = {
					codeVerifier,
					codeChallenge,
					codeChallengeMethod: flow["x-usePkce"] === "SHA-256" ? "S256" : "plain"
				};
				url.searchParams.set("code_challenge", codeChallenge);
				url.searchParams.set("code_challenge_method", pkce.codeChallengeMethod);
			}
		}
		if (flow["x-scalar-redirect-uri"].startsWith("/")) {
			const baseUrl = getInterpolatedServerUrl(activeServer) || window.location.origin + window.location.pathname;
			const redirectUri = makeUrlAbsolute(flow["x-scalar-redirect-uri"], { baseUrl });
			url.searchParams.set("redirect_uri", redirectUri);
		} else url.searchParams.set("redirect_uri", flow["x-scalar-redirect-uri"]);
		if (flow["x-scalar-security-query"]) Object.keys(flow["x-scalar-security-query"]).forEach((key) => {
			const value = flow["x-scalar-security-query"]?.[key];
			if (!value) return;
			url.searchParams.set(key, value);
		});
		url.searchParams.set("client_id", flow["x-scalar-client-id"]);
		url.searchParams.set("state", state);
		if (scopes) url.searchParams.set("scope", scopes);
		const authWindow = window.open(url, "openAuth2Window", "left=100,top=100,width=800,height=600");
		if (authWindow) return new Promise((resolve) => {
			const checkWindowClosed = setInterval(() => {
				let accessToken = null;
				let code = null;
				let error = null;
				let errorDescription = null;
				try {
					const urlParams = new URL(authWindow.location.href).searchParams;
					const tokenName = flow["x-tokenName"] || "access_token";
					accessToken = urlParams.get(tokenName);
					code = urlParams.get("code");
					error = urlParams.get("error");
					errorDescription = urlParams.get("error_description");
					const hashParams = new URLSearchParams(authWindow.location.href.split("#")[1]);
					accessToken ||= hashParams.get(tokenName);
					code ||= hashParams.get("code");
					error ||= hashParams.get("error");
					errorDescription ||= hashParams.get("error_description");
				} catch (_e) {}
				if (authWindow.closed || accessToken || code || error) {
					clearInterval(checkWindowClosed);
					authWindow.close();
					if (error) resolve([/* @__PURE__ */ new Error(`OAuth error: ${error}${errorDescription ? ` (${errorDescription})` : ""}`), null]);
					else if (accessToken) if (authWindow.location.href.match(/state=([^&]*)/)?.[1] === state) resolve([null, accessToken]);
					else resolve([/* @__PURE__ */ new Error("State mismatch"), null]);
					else if (code) if (new URL(authWindow.location.href).searchParams.get("state") === state) authorizeServers(flow, scopes, {
						code,
						pkce,
						proxyUrl
					}, activeServer).then(resolve);
					else resolve([/* @__PURE__ */ new Error("State mismatch"), null]);
					else {
						clearInterval(checkWindowClosed);
						resolve([/* @__PURE__ */ new Error("Window was closed without granting authorization"), null]);
					}
				}
			}, 200);
		});
		return [/* @__PURE__ */ new Error("Failed to open auth window"), null];
	} catch (_) {
		return [/* @__PURE__ */ new Error("Failed to authorize oauth2 flow"), null];
	}
};
/**
* Makes the BE authorization call to grab the token server to server
* Used for clientCredentials and authorizationCode
*/
var authorizeServers = async (flow, scopes, { code, pkce, proxyUrl } = {}, activeServer) => {
	if (!flow) return [/* @__PURE__ */ new Error("OAuth2 flow was not defined"), null];
	const formData = new URLSearchParams();
	formData.set("client_id", flow["x-scalar-client-id"]);
	if (scopes && (flow.type === "clientCredentials" || flow.type === "password")) formData.set("scope", scopes);
	if (flow.clientSecret && (!flow["x-scalar-credentials-location"] || flow["x-scalar-credentials-location"] === "body")) formData.set("client_secret", flow.clientSecret);
	if ("x-scalar-redirect-uri" in flow && flow["x-scalar-redirect-uri"]) formData.set("redirect_uri", flow["x-scalar-redirect-uri"]);
	if (code) {
		formData.set("code", code);
		formData.set("grant_type", "authorization_code");
		if (pkce) formData.set("code_verifier", pkce.codeVerifier);
	} else if (flow.type === "password") {
		formData.set("grant_type", "password");
		formData.set("username", flow.username);
		formData.set("password", flow.password);
	} else formData.set("grant_type", "client_credentials");
	if (flow["x-scalar-security-body"]) Object.entries(flow["x-scalar-security-body"]).forEach(([key, value]) => {
		if (value) formData.set(key, value);
	});
	try {
		const headers = { "Content-Type": "application/x-www-form-urlencoded" };
		if (flow.clientSecret && (!flow["x-scalar-credentials-location"] || flow["x-scalar-credentials-location"] === "header")) headers.Authorization = `Basic ${encode(`${flow["x-scalar-client-id"]}:${flow.clientSecret}`)}`;
		const tokenUrl = makeUrlAbsolute(flow.tokenUrl, getActiveServerBase(activeServer));
		const url = shouldUseProxy(proxyUrl, tokenUrl) ? `${proxyUrl}?${new URLSearchParams([["scalar_url", tokenUrl]]).toString()}` : tokenUrl;
		return [null, (await (await fetch(url, {
			method: "POST",
			headers,
			body: formData
		})).json())[flow["x-tokenName"] || "access_token"]];
	} catch {
		return [/* @__PURE__ */ new Error("Failed to get an access token. Please check your credentials."), null];
	}
};
//#endregion
export { authorizeOauth2, authorizeServers, generateCodeChallenge };

//# sourceMappingURL=oauth2.js.map