/**
 * OAuth credential management for AI providers.
 *
 * This module handles login, token refresh, and credential storage
 * for OAuth-based providers:
 * - GitHub Copilot
 *
 * Note: Anthropic OAuth was removed per TOS compliance (see docs/user-docs/claude-code-auth-compliance.md).
 * Use API keys or the local Claude Code CLI for Anthropic access.
 *
 * Note: Google Cloud Code Assist (google-gemini-cli) is not handled here.
 * The provider delegates to @google/gemini-cli-core, which reads
 * ~/.gemini/oauth_creds.json when present and owns any login flow it needs.
 * SF uses cli-core directly and does not spawn a separate provider CLI process.
 *
 * Note: OpenAI Codex (ChatGPT) is not handled here via OAuth flows.
 * The real `codex` CLI writes auth state to ~/.codex/auth.json after login.
 * We read that file directly — no PKCE, no callback server in our code.
 * Users authenticate with: codex auth login
 */

// GitHub Copilot
export {
	getGitHubCopilotBaseUrl,
	githubCopilotOAuthProvider,
	loginGitHubCopilot,
	normalizeDomain,
	refreshGitHubCopilotToken,
} from "./github-copilot.js";
// OpenAI Codex — shim provider (login defers to real `codex` CLI)
export { openaiCodexOAuthProvider } from "./openai-codex.js";

export * from "./types.js";

// ============================================================================
// Provider Registry
// ============================================================================

import { githubCopilotOAuthProvider } from "./github-copilot.js";
import { openaiCodexOAuthProvider } from "./openai-codex.js";
import type {
	OAuthCredentials,
	OAuthProviderId,
	OAuthProviderInterface,
} from "./types.js";

const BUILT_IN_OAUTH_PROVIDERS: OAuthProviderInterface[] = [
	githubCopilotOAuthProvider,
	openaiCodexOAuthProvider,
];

const oauthProviderRegistry = new Map<string, OAuthProviderInterface>(
	BUILT_IN_OAUTH_PROVIDERS.map((provider) => [provider.id, provider]),
);

/**
 * Get an OAuth provider by ID.
 *
 * Returns the provider if registered (built-in or custom), otherwise undefined.
 */
export function getOAuthProvider(
	id: OAuthProviderId,
): OAuthProviderInterface | undefined {
	return oauthProviderRegistry.get(id);
}

/**
 * Register a custom OAuth provider.
 *
 * Custom providers override built-ins with the same ID during the session.
 * Use `resetOAuthProviders` to restore built-ins.
 */
export function registerOAuthProvider(provider: OAuthProviderInterface): void {
	oauthProviderRegistry.set(provider.id, provider);
}

/**
 * Unregister an OAuth provider.
 *
 * If the provider is built-in, restores the built-in implementation.
 * Custom providers are removed completely.
 */
export function unregisterOAuthProvider(id: string): void {
	const builtInProvider = BUILT_IN_OAUTH_PROVIDERS.find(
		(provider) => provider.id === id,
	);
	if (builtInProvider) {
		oauthProviderRegistry.set(id, builtInProvider);
		return;
	}
	oauthProviderRegistry.delete(id);
}

/**
 * Reset OAuth providers to built-ins.
 *
 * Clears custom providers and restores only GitHub Copilot and OpenAI Codex.
 */
export function resetOAuthProviders(): void {
	oauthProviderRegistry.clear();
	for (const provider of BUILT_IN_OAUTH_PROVIDERS) {
		oauthProviderRegistry.set(provider.id, provider);
	}
}

/**
 * Get all registered OAuth providers.
 *
 * Returns both built-in and custom providers currently in the registry.
 */
export function getOAuthProviders(): OAuthProviderInterface[] {
	return Array.from(oauthProviderRegistry.values());
}

// ============================================================================
// High-level API (uses provider registry)
// ============================================================================

/**
 * Get API key for a provider from OAuth credentials, refreshing if expired.
 *
 * Returns the API key along with updated credentials (if refreshed), or null if no credentials exist.
 * Throws if the provider is unknown or token refresh fails.
 */
export async function getOAuthApiKey(
	providerId: OAuthProviderId,
	credentials: Record<string, OAuthCredentials>,
): Promise<{ newCredentials: OAuthCredentials; apiKey: string } | null> {
	const provider = getOAuthProvider(providerId);
	if (!provider) {
		throw new Error(`Unknown OAuth provider: ${providerId}`);
	}

	let creds = credentials[providerId];
	if (!creds) {
		return null;
	}

	// Refresh if expired
	if (Date.now() >= creds.expires) {
		try {
			creds = await provider.refreshToken(creds);
		} catch (error) {
			throw new Error(`Failed to refresh OAuth token for ${providerId}`, {
				cause: error,
			});
		}
	}

	const apiKey = provider.getApiKey(creds);
	return { newCredentials: creds, apiKey };
}
