import { oauthErrorHtml, oauthSuccessHtml } from "./oauth-page.js";
import { generatePKCE } from "./pkce.js";
//#region src/auth/oauth/vendor-pi-google/google-gemini-cli.ts
let _createServer = null;
let _httpImportPromise = null;
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) _httpImportPromise = import("node:http").then((m) => {
	_createServer = m.createServer;
});
const GEMINI_CLI_CLIENT_ID_ENV = "XOPC_GOOGLE_GEMINI_CLI_OAUTH_CLIENT_ID";
const GEMINI_CLI_CLIENT_SECRET_ENV = "XOPC_GOOGLE_GEMINI_CLI_OAUTH_CLIENT_SECRET";
function getGeminiCliOAuthClient() {
	const clientId = process.env[GEMINI_CLI_CLIENT_ID_ENV]?.trim();
	const clientSecret = process.env[GEMINI_CLI_CLIENT_SECRET_ENV]?.trim();
	if (!clientId || !clientSecret) throw new Error(`Google Gemini CLI OAuth requires ${GEMINI_CLI_CLIENT_ID_ENV} and ${GEMINI_CLI_CLIENT_SECRET_ENV}.`);
	return {
		clientId,
		clientSecret
	};
}
const REDIRECT_URI = "http://localhost:8085/oauth2callback";
const SCOPES = [
	"https://www.googleapis.com/auth/cloud-platform",
	"https://www.googleapis.com/auth/userinfo.email",
	"https://www.googleapis.com/auth/userinfo.profile"
];
const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
const TOKEN_URL = "https://oauth2.googleapis.com/token";
const CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
/**
* Start a local HTTP server to receive the OAuth callback
*/
async function getNodeCreateServer() {
	if (_createServer) return _createServer;
	if (_httpImportPromise) await _httpImportPromise;
	if (_createServer) return _createServer;
	throw new Error("Gemini CLI OAuth is only available in Node.js environments");
}
async function startCallbackServer() {
	const createServer = await getNodeCreateServer();
	return new Promise((resolve, reject) => {
		let settleWait;
		const waitForCodePromise = new Promise((resolveWait) => {
			let settled = false;
			settleWait = (value) => {
				if (settled) return;
				settled = true;
				resolveWait(value);
			};
		});
		const server = createServer((req, res) => {
			const url = new URL(req.url || "", `http://localhost:8085`);
			if (url.pathname === "/oauth2callback") {
				const code = url.searchParams.get("code");
				const state = url.searchParams.get("state");
				const error = url.searchParams.get("error");
				if (error) {
					res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
					res.end(oauthErrorHtml("Google authentication did not complete.", `Error: ${error}`));
					return;
				}
				if (code && state) {
					res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
					res.end(oauthSuccessHtml("Google authentication completed. You can close this window."));
					settleWait?.({
						code,
						state
					});
				} else {
					res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
					res.end(oauthErrorHtml("Missing code or state parameter."));
				}
			} else {
				res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
				res.end(oauthErrorHtml("Callback route not found."));
			}
		});
		server.on("error", (err) => {
			reject(err);
		});
		server.listen(8085, "127.0.0.1", () => {
			resolve({
				server,
				cancelWait: () => {
					settleWait?.(null);
				},
				waitForCode: () => waitForCodePromise
			});
		});
	});
}
/**
* Parse redirect URL to extract code and state
*/
function parseRedirectUrl(input) {
	const value = input.trim();
	if (!value) return {};
	try {
		const url = new URL(value);
		return {
			code: url.searchParams.get("code") ?? void 0,
			state: url.searchParams.get("state") ?? void 0
		};
	} catch {
		return {};
	}
}
const TIER_FREE = "free-tier";
const TIER_LEGACY = "legacy-tier";
const TIER_STANDARD = "standard-tier";
/**
* Wait helper for onboarding retries
*/
function wait(ms) {
	return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Get default tier from allowed tiers
*/
function getDefaultTier(allowedTiers) {
	if (!allowedTiers || allowedTiers.length === 0) return { id: TIER_LEGACY };
	return allowedTiers.find((t) => t.isDefault) ?? { id: TIER_LEGACY };
}
function isVpcScAffectedUser(payload) {
	if (!payload || typeof payload !== "object") return false;
	if (!("error" in payload)) return false;
	const error = payload.error;
	if (!error?.details || !Array.isArray(error.details)) return false;
	return error.details.some((detail) => detail.reason === "SECURITY_POLICY_VIOLATED");
}
/**
* Poll a long-running operation until completion
*/
async function pollOperation(operationName, headers, onProgress) {
	let attempt = 0;
	while (true) {
		if (attempt > 0) {
			onProgress?.(`Waiting for project provisioning (attempt ${attempt + 1})...`);
			await wait(5e3);
		}
		const response = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal/${operationName}`, {
			method: "GET",
			headers
		});
		if (!response.ok) throw new Error(`Failed to poll operation: ${response.status} ${response.statusText}`);
		const data = await response.json();
		if (data.done) return data;
		attempt += 1;
	}
}
/**
* Discover or provision a Google Cloud project for the user
*/
async function discoverProject(accessToken, onProgress) {
	const envProjectId = process.env.GOOGLE_CLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT_ID;
	const headers = {
		Authorization: `Bearer ${accessToken}`,
		"Content-Type": "application/json",
		"User-Agent": "google-api-nodejs-client/9.15.1",
		"X-Goog-Api-Client": "gl-node/22.17.0"
	};
	onProgress?.("Checking for existing Cloud Code Assist project...");
	const loadResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`, {
		method: "POST",
		headers,
		body: JSON.stringify({
			cloudaicompanionProject: envProjectId,
			metadata: {
				ideType: "IDE_UNSPECIFIED",
				platform: "PLATFORM_UNSPECIFIED",
				pluginType: "GEMINI",
				duetProject: envProjectId
			}
		})
	});
	let data;
	if (!loadResponse.ok) {
		let errorPayload;
		try {
			errorPayload = await loadResponse.clone().json();
		} catch {
			errorPayload = void 0;
		}
		if (isVpcScAffectedUser(errorPayload)) data = { currentTier: { id: TIER_STANDARD } };
		else {
			const errorText = await loadResponse.text();
			throw new Error(`loadCodeAssist failed: ${loadResponse.status} ${loadResponse.statusText}: ${errorText}`);
		}
	} else data = await loadResponse.json();
	if (data.currentTier) {
		if (data.cloudaicompanionProject) return data.cloudaicompanionProject;
		if (envProjectId) return envProjectId;
		throw new Error("This account requires setting the GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID environment variable. See https://goo.gle/gemini-cli-auth-docs#workspace-gca");
	}
	const tierId = getDefaultTier(data.allowedTiers)?.id ?? TIER_FREE;
	if (tierId !== TIER_FREE && !envProjectId) throw new Error("This account requires setting the GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID environment variable. See https://goo.gle/gemini-cli-auth-docs#workspace-gca");
	onProgress?.("Provisioning Cloud Code Assist project (this may take a moment)...");
	const onboardBody = {
		tierId,
		metadata: {
			ideType: "IDE_UNSPECIFIED",
			platform: "PLATFORM_UNSPECIFIED",
			pluginType: "GEMINI"
		}
	};
	if (tierId !== TIER_FREE && envProjectId) {
		onboardBody.cloudaicompanionProject = envProjectId;
		onboardBody.metadata.duetProject = envProjectId;
	}
	const onboardResponse = await fetch(`${CODE_ASSIST_ENDPOINT}/v1internal:onboardUser`, {
		method: "POST",
		headers,
		body: JSON.stringify(onboardBody)
	});
	if (!onboardResponse.ok) {
		const errorText = await onboardResponse.text();
		throw new Error(`onboardUser failed: ${onboardResponse.status} ${onboardResponse.statusText}: ${errorText}`);
	}
	let lroData = await onboardResponse.json();
	if (!lroData.done && lroData.name) lroData = await pollOperation(lroData.name, headers, onProgress);
	const projectId = lroData.response?.cloudaicompanionProject?.id;
	if (projectId) return projectId;
	if (envProjectId) return envProjectId;
	throw new Error("Could not discover or provision a Google Cloud project. Try setting the GOOGLE_CLOUD_PROJECT or GOOGLE_CLOUD_PROJECT_ID environment variable. See https://goo.gle/gemini-cli-auth-docs#workspace-gca");
}
/**
* Get user email from the access token
*/
async function getUserEmail(accessToken) {
	try {
		const response = await fetch("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", { headers: { Authorization: `Bearer ${accessToken}` } });
		if (response.ok) return (await response.json()).email;
	} catch {}
}
/**
* Refresh Google Cloud Code Assist token
*/
async function refreshGoogleCloudToken(refreshToken, projectId) {
	const { clientId, clientSecret } = getGeminiCliOAuthClient();
	const response = await fetch(TOKEN_URL, {
		method: "POST",
		headers: { "Content-Type": "application/x-www-form-urlencoded" },
		body: new URLSearchParams({
			client_id: clientId,
			client_secret: clientSecret,
			refresh_token: refreshToken,
			grant_type: "refresh_token"
		})
	});
	if (!response.ok) {
		const error = await response.text();
		throw new Error(`Google Cloud token refresh failed: ${error}`);
	}
	const data = await response.json();
	return {
		refresh: data.refresh_token || refreshToken,
		access: data.access_token,
		expires: Date.now() + data.expires_in * 1e3 - 300 * 1e3,
		projectId
	};
}
/**
* Login with Gemini CLI (Google Cloud Code Assist) OAuth
*
* @param onAuth - Callback with URL and optional instructions
* @param onProgress - Optional progress callback
* @param onManualCodeInput - Optional promise that resolves with user-pasted redirect URL.
*                            Races with browser callback - whichever completes first wins.
*/
async function loginGeminiCli(onAuth, onProgress, onManualCodeInput) {
	const { verifier, challenge } = await generatePKCE();
	const { clientId, clientSecret } = getGeminiCliOAuthClient();
	onProgress?.("Starting local server for OAuth callback...");
	const server = await startCallbackServer();
	let code;
	try {
		onAuth({
			url: `${AUTH_URL}?${new URLSearchParams({
				client_id: clientId,
				response_type: "code",
				redirect_uri: REDIRECT_URI,
				scope: SCOPES.join(" "),
				code_challenge: challenge,
				code_challenge_method: "S256",
				state: verifier,
				access_type: "offline",
				prompt: "consent"
			}).toString()}`,
			instructions: "Complete the sign-in in your browser."
		});
		onProgress?.("Waiting for OAuth callback...");
		if (onManualCodeInput) {
			let manualInput;
			let manualError;
			const manualPromise = onManualCodeInput().then((input) => {
				manualInput = input;
				server.cancelWait();
			}).catch((err) => {
				manualError = err instanceof Error ? err : new Error(String(err));
				server.cancelWait();
			});
			const result = await server.waitForCode();
			if (manualError) throw manualError;
			if (result?.code) {
				if (result.state !== verifier) throw new Error("OAuth state mismatch - possible CSRF attack");
				code = result.code;
			} else if (manualInput) {
				const parsed = parseRedirectUrl(manualInput);
				if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch - possible CSRF attack");
				code = parsed.code;
			}
			if (!code) {
				await manualPromise;
				if (manualError) throw manualError;
				if (manualInput) {
					const parsed = parseRedirectUrl(manualInput);
					if (parsed.state && parsed.state !== verifier) throw new Error("OAuth state mismatch - possible CSRF attack");
					code = parsed.code;
				}
			}
		} else {
			const result = await server.waitForCode();
			if (result?.code) {
				if (result.state !== verifier) throw new Error("OAuth state mismatch - possible CSRF attack");
				code = result.code;
			}
		}
		if (!code) throw new Error("No authorization code received");
		onProgress?.("Exchanging authorization code for tokens...");
		const tokenResponse = await fetch(TOKEN_URL, {
			method: "POST",
			headers: { "Content-Type": "application/x-www-form-urlencoded" },
			body: new URLSearchParams({
				client_id: clientId,
				client_secret: clientSecret,
				code,
				grant_type: "authorization_code",
				redirect_uri: REDIRECT_URI,
				code_verifier: verifier
			})
		});
		if (!tokenResponse.ok) {
			const error = await tokenResponse.text();
			throw new Error(`Token exchange failed: ${error}`);
		}
		const tokenData = await tokenResponse.json();
		if (!tokenData.refresh_token) throw new Error("No refresh token received. Please try again.");
		onProgress?.("Getting user info...");
		const email = await getUserEmail(tokenData.access_token);
		const projectId = await discoverProject(tokenData.access_token, onProgress);
		const expiresAt = Date.now() + tokenData.expires_in * 1e3 - 300 * 1e3;
		return {
			refresh: tokenData.refresh_token,
			access: tokenData.access_token,
			expires: expiresAt,
			projectId,
			email
		};
	} finally {
		server.server.close();
	}
}
const geminiCliOAuthProvider = {
	id: "google-gemini-cli",
	name: "Google Cloud Code Assist (Gemini CLI)",
	usesCallbackServer: true,
	async login(callbacks) {
		return loginGeminiCli(callbacks.onAuth, callbacks.onProgress, callbacks.onManualCodeInput);
	},
	async refreshToken(credentials) {
		const creds = credentials;
		if (!creds.projectId) throw new Error("Google Cloud credentials missing projectId");
		return refreshGoogleCloudToken(creds.refresh, creds.projectId);
	},
	getApiKey(credentials) {
		const creds = credentials;
		return JSON.stringify({
			token: creds.access,
			projectId: creds.projectId
		});
	}
};
//#endregion
export { geminiCliOAuthProvider, loginGeminiCli, refreshGoogleCloudToken };

//# sourceMappingURL=google-gemini-cli.js.map