import { o as createSubsystemLogger } from "./entry.js";
import { t as runCommandWithTimeout } from "./exec-CmvuUufz.js";
import { o as extractGogClientCredentials } from "./gmail-setup-utils-CqttLGxc.js";
import fs from "node:fs";
import fs$1 from "fs/promises";
import os from "os";
import path from "path";

//#region src/hooks/gog-credentials.ts
/**
* Google OAuth credential storage manager
* Handles per-session credential isolation with token refresh
*/
const log = createSubsystemLogger("gog-credentials");
/** Parsed client credentials from the config-specified JSON file */
let configClientCredentials = null;
/**
* Set the path to the Google client credentials JSON file (from config).
* Called once during gateway startup if `hooks.gogOAuth.googleClientCredentialsFile` is set.
* The file format matches the Google Cloud Console download (supports "installed" and "web" types).
*/
function setGoogleClientCredentialsFile(filePath) {
	try {
		const raw = fs.readFileSync(filePath, "utf-8");
		const parsed = JSON.parse(raw);
		const installed = parsed.installed;
		const web = parsed.web;
		const source = installed ?? web ?? parsed;
		const clientType = installed ? "installed" : web ? "web" : "unknown";
		const clientId = source.client_id;
		const clientSecret = source.client_secret;
		if (typeof clientId === "string" && typeof clientSecret === "string" && clientId && clientSecret) {
			configClientCredentials = {
				clientId,
				clientSecret,
				clientType
			};
			log.info(`Loaded Google client credentials from config: ${filePath} (type: ${clientType})`);
		} else log.warn(`Config file ${filePath} exists but does not contain valid client_id/client_secret`);
	} catch (err) {
		log.warn(`Failed to read Google client credentials file: ${filePath}: ${String(err)}`);
	}
}
/**
* Get the credentials directory for an agent (new canonical path).
* Aligned with GoogleAuthProvider's `auth-credentials/google/` layout.
*/
function getCredentialsDir(agentId) {
	const homeDir = os.homedir();
	return path.join(homeDir, ".minion", "agents", agentId, "auth-credentials", "google");
}
/**
* Get the legacy credentials directory for an agent (gog-credentials/).
* Used only for fallback reads and migration.
*/
function getLegacyCredentialsDir(agentId) {
	const homeDir = os.homedir();
	return path.join(homeDir, ".minion", "agents", agentId, "gog-credentials");
}
/**
* Build a sanitized credential filename from session key and email.
*/
function buildCredentialFilename(sessionKey, email) {
	return `${sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_")}_${email.replace(/[^a-zA-Z0-9@._-]/g, "_")}.json`;
}
/**
* Get the credentials file path for a session
*/
function getCredentialsPath(agentId, sessionKey, email) {
	const dir = getCredentialsDir(agentId);
	return path.join(dir, buildCredentialFilename(sessionKey, email));
}
/**
* Ensure credentials directory exists
*/
async function ensureCredentialsDir(agentId) {
	const dir = getCredentialsDir(agentId);
	await fs$1.mkdir(dir, {
		recursive: true,
		mode: 448
	});
}
/**
* Load session credentials if they exist.
* Checks the canonical `auth-credentials/google/` path first, then falls back
* to the legacy `gog-credentials/` path (and migrates if found).
*/
async function loadSessionCredentials(agentId, sessionKey, email) {
	const safeSessionKey = sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
	async function tryLoadFromDir(dir) {
		if (email) {
			const safeEmail = email.replace(/[^a-zA-Z0-9@._-]/g, "_");
			const credPath = path.join(dir, `${safeSessionKey}_${safeEmail}.json`);
			try {
				const data = await fs$1.readFile(credPath, "utf-8");
				const creds = JSON.parse(data);
				creds.filePath = credPath;
				return creds;
			} catch {}
		}
		const pattern = `${safeSessionKey}_`;
		try {
			const matchingFiles = (await fs$1.readdir(dir)).filter((f) => f.startsWith(pattern));
			if (matchingFiles.length > 0) {
				const credPath = path.join(dir, matchingFiles[0]);
				const data = await fs$1.readFile(credPath, "utf-8");
				const creds = JSON.parse(data);
				creds.filePath = credPath;
				return creds;
			}
		} catch {}
		return null;
	}
	try {
		const newDir = getCredentialsDir(agentId);
		const result = await tryLoadFromDir(newDir);
		if (result) return result;
		const legacyResult = await tryLoadFromDir(getLegacyCredentialsDir(agentId));
		if (legacyResult && legacyResult.filePath) {
			const filename = path.basename(legacyResult.filePath);
			await fs$1.mkdir(newDir, {
				recursive: true,
				mode: 448
			});
			const newPath = path.join(newDir, filename);
			await fs$1.writeFile(newPath, JSON.stringify(legacyResult, null, 2), { mode: 384 });
			try {
				await fs$1.unlink(legacyResult.filePath);
				log.info(`Migrated credential from gog-credentials/ to auth-credentials/google/ for session ${safeSessionKey}`);
			} catch {
				log.warn(`Migration: new file written but could not delete old file at ${legacyResult.filePath}`);
			}
			legacyResult.filePath = newPath;
			return legacyResult;
		}
		return null;
	} catch (error) {
		log.error(`Error loading session credentials: ${error instanceof Error ? error.message : String(error)}`);
		return null;
	}
}
/**
* Save credentials with secure file permissions (0600)
*/
async function saveSessionCredentials(credentials) {
	await ensureCredentialsDir(credentials.agentId);
	const credPath = getCredentialsPath(credentials.agentId, credentials.sessionKey, credentials.email);
	const data = JSON.stringify(credentials, null, 2);
	await fs$1.writeFile(credPath, data, { mode: 384 });
	return credPath;
}
/**
* Refresh access token using refresh token
*/
async function refreshAccessToken(credentials) {
	const response = await fetch("https://oauth2.googleapis.com/token", {
		method: "POST",
		headers: { "Content-Type": "application/x-www-form-urlencoded" },
		body: new URLSearchParams({
			client_id: getGoogleClientId(),
			client_secret: getGoogleClientSecret(),
			refresh_token: credentials.refreshToken,
			grant_type: "refresh_token"
		})
	});
	if (!response.ok) {
		const errorBody = await response.text();
		let parsed = {};
		try {
			parsed = JSON.parse(errorBody);
		} catch {}
		if (parsed.error === "invalid_grant") throw new Error(`Google OAuth refresh failed: invalid_grant — ${parsed.error_description || "token expired or revoked"}.\nCommon causes:
  - OAuth consent screen is in 'Testing' mode (tokens expire after 7 days)
  - User revoked access in Google Account settings
  - Token was already used or replaced
Fix: Re-authenticate with the gog-auth-start tool. If tokens keep expiring after 7 days,
move your OAuth consent screen from Testing to Production in Google Cloud Console.`);
		throw new Error(`Failed to refresh Google OAuth token (${response.status}): ${parsed.error_description || parsed.error || errorBody}`);
	}
	const tokenData = await response.json();
	const updatedCredentials = {
		...credentials,
		accessToken: tokenData.access_token,
		expiresAt: Date.now() + tokenData.expires_in * 1e3
	};
	await saveSessionCredentials(updatedCredentials);
	return updatedCredentials;
}
/**
* Check if access token is expired or about to expire (within 5 minutes)
*/
function isTokenExpired(credentials) {
	return Date.now() >= credentials.expiresAt - 300 * 1e3;
}
/**
* Get valid credentials, refreshing if necessary.
* Returns a discriminated CredentialResult so callers can distinguish
* "no credentials" from "refresh failed" and surface actionable errors.
*/
async function getValidCredentials(agentId, sessionKey, email) {
	const credentials = await loadSessionCredentials(agentId, sessionKey, email);
	if (!credentials) return {
		credentials: null,
		error: "No Google credentials found for this session",
		refreshFailed: false
	};
	if (isTokenExpired(credentials)) try {
		return { credentials: await refreshAccessToken(credentials) };
	} catch (error) {
		const msg = error instanceof Error ? error.message : String(error);
		log.warn(`Token refresh failed: ${msg}`);
		return {
			credentials: null,
			error: msg,
			refreshFailed: true
		};
	}
	return { credentials };
}
/**
* Revoke credentials with Google and delete local file
*/
async function revokeCredentials(agentId, sessionKey, email) {
	const credentials = await loadSessionCredentials(agentId, sessionKey, email);
	if (!credentials) return;
	try {
		await fetch(`https://oauth2.googleapis.com/revoke?token=${credentials.refreshToken || credentials.accessToken}`, {
			method: "POST",
			headers: { "Content-Type": "application/x-www-form-urlencoded" }
		});
	} catch (error) {
		log.error(`Failed to revoke token with Google: ${error instanceof Error ? error.message : String(error)}`);
	}
	if (credentials.filePath) try {
		await fs$1.unlink(credentials.filePath);
	} catch (error) {
		log.error(`Failed to delete credentials file: ${error instanceof Error ? error.message : String(error)}`);
	}
}
/** Cached credentials extracted from gog CLI JSON file */
let cachedFileCredentials;
function getFileCredentials() {
	if (cachedFileCredentials !== void 0) return cachedFileCredentials;
	cachedFileCredentials = extractGogClientCredentials();
	return cachedFileCredentials;
}
/**
* Get Google OAuth client ID.
* Priority: env var GOOGLE_CLIENT_ID > gog CLI credentials.json file
*/
function getGoogleClientId() {
	if (configClientCredentials) return configClientCredentials.clientId;
	if (process.env.GOOGLE_CLIENT_ID) return process.env.GOOGLE_CLIENT_ID;
	const fileCreds = getFileCredentials();
	if (fileCreds) return fileCreds.clientId;
	throw new Error("Google OAuth client ID not found. Checked (in order):\n  1. hooks.gogOAuth.googleClientCredentialsFile in minion.json\n  2. GOOGLE_CLIENT_ID environment variable\n  3. ~/.config/gogcli/credentials.json\nSet one of these to your Google Cloud Console OAuth client credentials.");
}
/**
* Get Google OAuth client secret.
* Priority: env var GOOGLE_CLIENT_SECRET > gog CLI credentials.json file
*/
function getGoogleClientSecret() {
	if (configClientCredentials) return configClientCredentials.clientSecret;
	if (process.env.GOOGLE_CLIENT_SECRET) return process.env.GOOGLE_CLIENT_SECRET;
	const fileCreds = getFileCredentials();
	if (fileCreds) return fileCreds.clientSecret;
	throw new Error("Google OAuth client secret not found. Checked (in order):\n  1. hooks.gogOAuth.googleClientCredentialsFile in minion.json\n  2. GOOGLE_CLIENT_SECRET environment variable\n  3. ~/.config/gogcli/credentials.json\nSet one of these to your Google Cloud Console OAuth client credentials.");
}
/**
* Get the detected Google OAuth client type from the credentials file.
* Returns "unknown" if no credentials file was loaded or type couldn't be determined.
*/
function getGoogleClientType() {
	return configClientCredentials?.clientType ?? "unknown";
}
/**
* Import OAuth tokens into the gog CLI keyring.
* Core sync logic — writes a temp token file, runs `gog auth tokens import`, cleans up.
* Caller is responsible for ensuring gog is installed and available.
*
* @param credentials - The OAuth credentials to import
* @param env - Optional environment override (e.g. with GOG_KEYRING_BACKEND/PASSWORD).
*              Defaults to process.env if not provided.
* @returns Result indicating success or failure with error detail
*/
async function importTokensToGogKeyring(credentials, env) {
	const tmpDir = os.tmpdir();
	const tmpFile = path.join(tmpDir, `gog-token-import-${Date.now()}.json`);
	const tokenData = {
		email: credentials.email,
		access_token: credentials.accessToken,
		refresh_token: credentials.refreshToken,
		token_type: "Bearer",
		expiry: new Date(credentials.expiresAt).toISOString()
	};
	fs.writeFileSync(tmpFile, JSON.stringify(tokenData, null, 2), { mode: 384 });
	try {
		const result = await runCommandWithTimeout([
			"gog",
			"auth",
			"tokens",
			"import",
			tmpFile,
			"--account",
			credentials.email
		], {
			timeoutMs: 1e4,
			env
		});
		if (result.code === 0) {
			log.info(`Synced tokens to gog CLI keyring for ${credentials.email}`);
			return { success: true };
		} else {
			const detail = result.stderr || result.stdout;
			log.warn(`gog auth tokens import failed (code=${result.code}): ${detail}`);
			return {
				success: false,
				error: `exit code ${result.code}: ${detail}`
			};
		}
	} finally {
		try {
			fs.unlinkSync(tmpFile);
		} catch {}
	}
}
/**
* Sync OAuth tokens to gog CLI keyring so `gog gmail ...` commands work.
* Best-effort: logs errors but never throws. Returns result so callers can
* surface warnings to the user.
*/
async function syncToGogKeyring(credentials) {
	try {
		if ((await runCommandWithTimeout(["which", "gog"], { timeoutMs: 2e3 })).code !== 0) return {
			success: false,
			error: "gog CLI not installed"
		};
		const result = await importTokensToGogKeyring(credentials);
		if (!result.success && result.error) {
			if (/unknown (command|flag|subcommand)|no such command|unrecognized/i.test(result.error)) {
				const match = (await runCommandWithTimeout(["gog", "--version"], { timeoutMs: 3e3 })).stdout.match(/v(\d+)\.(\d+)\.(\d+)/);
				if (match) {
					const [, major, minor] = match.map(Number);
					if (major === 0 && minor < 11) {
						const msg = `gog CLI v${major}.${minor} is too old for 'auth tokens import' (requires >= v0.11.0)`;
						log.warn(`${msg}. Skipping keyring sync.`);
						return {
							success: false,
							error: msg
						};
					}
				}
			}
		}
		return result;
	} catch (error) {
		const msg = error instanceof Error ? error.message : String(error);
		log.warn(`Failed to sync tokens to gog CLI keyring: ${error instanceof Error ? error.message : String(error)}`);
		return {
			success: false,
			error: msg
		};
	}
}

//#endregion
export { getGoogleClientType as a, importTokensToGogKeyring as c, syncToGogKeyring as d, getGoogleClientSecret as i, revokeCredentials as l, getCredentialsDir as n, getLegacyCredentialsDir as o, getGoogleClientId as r, getValidCredentials as s, buildCredentialFilename as t, setGoogleClientCredentialsFile as u };