"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeminiAuthService = void 0;
const crypto_1 = __importDefault(require("crypto"));
const open_1 = __importDefault(require("open"));
const server_1 = require("../utils/server");
const ui_1 = require("../utils/ui");
/**
 * Gemini CLI (Google Cloud Code Assist) OAuth Service
 * Uses standard OAuth2 Authorization Code flow (no PKCE)
 */
class GeminiAuthService {
    constructor(clientId, clientSecret, authorizeUrl, tokenUrl, userInfoUrl, scopes) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.authorizeUrl = authorizeUrl;
        this.tokenUrl = tokenUrl;
        this.userInfoUrl = userInfoUrl;
        this.scopes = scopes;
    }
    /**
     * Build Gemini authorization URL
     */
    buildAuthUrl(redirectUri, state) {
        const params = new URLSearchParams({
            client_id: this.clientId,
            response_type: "code",
            redirect_uri: redirectUri,
            scope: this.scopes.join(" "),
            state: state,
            access_type: "offline",
            prompt: "consent",
        });
        return `${this.authorizeUrl}?${params.toString()}`;
    }
    /**
     * Exchange authorization code for tokens
     */
    async exchangeCode(code, redirectUri) {
        const response = await fetch(this.tokenUrl, {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
                Accept: "application/json",
            },
            body: new URLSearchParams({
                grant_type: "authorization_code",
                client_id: this.clientId,
                client_secret: this.clientSecret,
                code: code,
                redirect_uri: redirectUri,
            }),
        });
        if (!response.ok) {
            const error = await response.text();
            throw new Error(`Token exchange failed: ${error}`);
        }
        return await response.json();
    }
    /**
     * Fetch project ID from Google Cloud Code Assist
     */
    async fetchProjectId(accessToken) {
        const response = await fetch("https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", {
            method: "POST",
            headers: {
                Authorization: `Bearer ${accessToken}`,
                "Content-Type": "application/json",
                "User-Agent": "google-api-nodejs-client/9.15.1",
                "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
                "Client-Metadata": JSON.stringify(this.getOAuthClientMetadata())
            },
            body: JSON.stringify({
                metadata: this.getOAuthClientMetadata(),
                mode: 1
            })
        });
        if (!response.ok) {
            const error = await response.text();
            throw new Error(`Failed to fetch project ID: ${error}`);
        }
        const data = await response.json();
        // Extract project ID
        let projectId = "";
        if (typeof data.cloudaicompanionProject === "string") {
            projectId = data.cloudaicompanionProject.trim();
        }
        else if (data.cloudaicompanionProject?.id) {
            projectId = data.cloudaicompanionProject.id.trim();
        }
        if (!projectId) {
            throw new Error("No project ID found in response");
        }
        return projectId;
    }
    /**
     * Get user info from Google
     */
    async getUserInfo(accessToken) {
        const response = await fetch(`${this.userInfoUrl}?alt=json`, {
            headers: new Headers({
                Authorization: `Bearer ${accessToken}`,
                Accept: "application/json",
            }),
        });
        if (!response.ok) {
            const error = await response.text();
            throw new Error(`Failed to get user info: ${error}`);
        }
        return await response.json();
    }
    /**
     * Save Gemini tokens (to be implemented by account manager)
     */
    async saveTokens(tokens, userInfo, projectId) {
        // This will be implemented in the account manager
        // For now, just return the data
        return {
            accessToken: tokens.access_token,
            refreshToken: tokens.refresh_token,
            expiresIn: tokens.expires_in,
            scope: tokens.scope,
            email: userInfo.email,
            projectId: projectId,
        };
    }
    /**
     * Get OAuth client metadata (platform detection)
     */
    getOAuthClientMetadata() {
        // Simplified version - in real implementation, detect platform
        return { ideType: 9, platform: 3, pluginType: 2 }; // linux x64
    }
    /**
     * Complete Gemini OAuth flow
     */
    async connect() {
        const spinner = (0, ui_1.createSpinner)("Starting Gemini OAuth...").start();
        try {
            spinner.updateText("Starting local server...");
            // Start local server for callback
            let callbackParams = null;
            const { port, close } = await (0, server_1.startLocalServer)((params) => {
                callbackParams = params;
            });
            const redirectUri = `http://localhost:${port}/callback`;
            spinner.succeed(`Local server started on port ${port}`);
            // Generate state
            const state = crypto_1.default.randomBytes(32).toString("base64url");
            // Build authorization URL
            const authUrl = this.buildAuthUrl(redirectUri, state);
            console.log("\nOpening browser for Google authentication...");
            console.log(`If browser doesn't open, visit:\n${authUrl}\n`);
            // Open browser
            await (0, open_1.default)(authUrl);
            // Wait for callback
            spinner.updateText("Waiting for Google authorization...");
            await new Promise((resolve, reject) => {
                const timeout = setTimeout(() => {
                    reject(new Error("Authentication timeout (5 minutes)"));
                }, 300000);
                const checkInterval = setInterval(() => {
                    if (callbackParams) {
                        clearInterval(checkInterval);
                        clearTimeout(timeout);
                        resolve();
                    }
                }, 100);
            });
            close();
            if (callbackParams.error) {
                throw new Error(callbackParams.error_description || callbackParams.error);
            }
            if (!callbackParams.code) {
                throw new Error("No authorization code received");
            }
            spinner.updateText("Exchanging code for tokens...");
            // Exchange code for tokens
            const tokens = await this.exchangeCode(callbackParams.code, redirectUri);
            spinner.updateText("Fetching user info...");
            // Get user info
            const userInfo = await this.getUserInfo(tokens.access_token);
            spinner.updateText("Fetching project ID...");
            // Fetch project ID
            const projectId = await this.fetchProjectId(tokens.access_token);
            spinner.updateText("Saving tokens...");
            // Save tokens
            await this.saveTokens(tokens, userInfo, projectId);
            spinner.succeed(`Gemini connected successfully! (${userInfo.email}, Project: ${projectId})`);
            return true;
        }
        catch (error) {
            spinner.fail(`Failed: ${error.message}`);
            throw error;
        }
    }
}
exports.GeminiAuthService = GeminiAuthService;
//# sourceMappingURL=gemini-auth.js.map