/**
 * auth.ts — MCP HTTP transport authentication helpers.
 *
 * Purpose: build transport options (static headers or OAuthClientProvider)
 * from MCP server config entries so HTTP transports authenticate correctly.
 *
 * Consumer: McpConnectionManager.getOrConnect() for HTTP transport setup.
 */
import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
import type { OAuthConfig } from "./config.js";

export interface AuthConfig {
	headers?: Record<string, string>;
	oauth?: OAuthConfig;
}

export interface HttpTransportOptions {
	authProvider?: OAuthClientProvider;
	requestInit?: { headers: Record<string, string> };
}

function resolveEnvValue(value: string): string {
	return value.replace(
		/\$\{([^}]+)\}/g,
		(_match: string, varName: string) => process.env[varName] ?? "",
	);
}

function resolveHeaders(raw: Record<string, string>): Record<string, string> {
	const resolved: Record<string, string> = {};
	for (const [key, value] of Object.entries(raw)) {
		resolved[key] = typeof value === "string" ? resolveEnvValue(value) : value;
	}
	return resolved;
}

/**
 * Create a minimal OAuthClientProvider for CLI / headless use.
 *
 * Purpose: support pre-configured client credentials and in-memory token
 * storage for server-to-server and pre-authed OAuth scenarios.
 *
 * Consumer: buildHttpTransportOpts when the server config has an oauth block.
 */
export function createCliOAuthProvider(
	config: OAuthConfig,
): OAuthClientProvider {
	let storedTokens:
		| Parameters<OAuthClientProvider["saveTokens"]>[0]
		| undefined;
	let storedCodeVerifier = "";
	return {
		get redirectUrl() {
			return config.redirectUrl ?? "http://localhost:0/callback";
		},
		get clientMetadata() {
			return {
				redirect_uris: [config.redirectUrl ?? "http://localhost:0/callback"],
				client_name: "sf",
				...(config.scopes ? { scope: config.scopes.join(" ") } : {}),
			};
		},
		clientInformation() {
			return {
				client_id: config.clientId,
				...(config.clientSecret ? { client_secret: config.clientSecret } : {}),
			};
		},
		tokens() {
			return storedTokens;
		},
		saveTokens(tokens) {
			storedTokens = tokens;
		},
		redirectToAuthorization(authorizationUrl) {
			console.error(
				`[MCP OAuth] Authorization required. Visit:\n  ${authorizationUrl.toString()}`,
			);
		},
		saveCodeVerifier(codeVerifier) {
			storedCodeVerifier = codeVerifier;
		},
		codeVerifier() {
			return storedCodeVerifier;
		},
	};
}

/**
 * Build StreamableHTTPClientTransport options from an MCP server config's
 * auth-related fields.
 *
 * Supports two strategies:
 *  1. headers — static Authorization headers with ${VAR} env resolution.
 *  2. oauth   — OAuthClientProvider for servers that implement MCP OAuth.
 * When both are provided, oauth takes precedence.
 *
 * Purpose: centralize HTTP auth config translation to avoid duplication
 * across connection setup paths.
 *
 * Consumer: McpConnectionManager.getOrConnect() for HTTP transport setup.
 */
export function buildHttpTransportOpts(
	authConfig: AuthConfig,
): HttpTransportOptions {
	const opts: HttpTransportOptions = {};
	if (authConfig.oauth) {
		opts.authProvider = createCliOAuthProvider(authConfig.oauth);
		return opts;
	}
	if (authConfig.headers && Object.keys(authConfig.headers).length > 0) {
		opts.requestInit = {
			headers: resolveHeaders(authConfig.headers),
		};
	}
	return opts;
}
