"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AntigravityService = 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");
/**
 * Antigravity OAuth Service
 * Uses standard OAuth2 Authorization Code flow (similar to Gemini)
 */
class AntigravityService {
    constructor(clientId, clientSecret, authorizeUrl, tokenUrl, userInfoUrl, scopes, loadCodeAssistEndpoint, onboardUserEndpoint, loadCodeAssistUserAgent, loadCodeAssistApiClient, loadCodeAssistClientMetadata) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.authorizeUrl = authorizeUrl;
        this.tokenUrl = tokenUrl;
        this.userInfoUrl = userInfoUrl;
        this.scopes = scopes;
        this.loadCodeAssistEndpoint = loadCodeAssistEndpoint;
        this.onboardUserEndpoint = onboardUserEndpoint;
        this.loadCodeAssistUserAgent = loadCodeAssistUserAgent;
        this.loadCodeAssistApiClient = loadCodeAssistApiClient;
        this.loadCodeAssistClientMetadata = loadCodeAssistClientMetadata;
    }
    /**
     * Build Antigravity 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();
    }
    /**
     * Get user info from Google
     */
    async getUserInfo(accessToken) {
        const response = await fetch(`${this.userInfoUrl}?alt=json`, {
            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();
    }
    /**
     * Get common headers for Antigravity API calls
     */
    getApiHeaders(accessToken) {
        return {
            "Authorization": `Bearer ${accessToken}`,
            "Content-Type": "application/json",
            "User-Agent": this.loadCodeAssistUserAgent,
            "X-Goog-Api-Client": this.loadCodeAssistApiClient,
            "Client-Metadata": this.loadCodeAssistClientMetadata,
        };
    }
    /**
     * Get metadata object for loadCodeAssist / onboardUser API calls.
     * Uses numeric enum values matching Antigravity binary ClientMetadata.
     */
    getMetadata() {
        // Simplified version - in real implementation, detect platform
        return { ideType: 9, platform: 3, pluginType: 2 }; // linux x64
    }
    /**
     * Fetch Project ID and Tier from loadCodeAssist API
     */
    async loadCodeAssist(accessToken) {
        const response = await fetch(this.loadCodeAssistEndpoint, {
            method: "POST",
            headers: this.getApiHeaders(accessToken),
            body: JSON.stringify({ metadata: this.getMetadata() }),
        });
        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`Failed to load code assist: ${errorText}`);
        }
        const data = await response.json();
        // Extract project ID
        let projectId = data.cloudaicompanionProject;
        if (typeof projectId === 'object' && projectId !== null && projectId.id) {
            projectId = projectId.id;
        }
        // Extract tier ID (default to legacy-tier)
        let tierId = "legacy-tier";
        if (Array.isArray(data.allowedTiers)) {
            for (const tier of data.allowedTiers) {
                if (tier.isDefault && tier.id) {
                    tierId = tier.id.trim();
                    break;
                }
            }
        }
        return { projectId, tierId, raw: data };
    }
    /**
     * Onboard user to enable Gemini Code Assist for the project
     */
    async onboardUser(accessToken, projectId, tierId) {
        const response = await fetch(this.onboardUserEndpoint, {
            method: "POST",
            headers: this.getApiHeaders(accessToken),
            body: JSON.stringify({ tierId, metadata: this.getMetadata() }),
        });
        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`Failed to onboard user: ${errorText}`);
        }
        return await response.json();
    }
    /**
     * Complete onboarding flow with retry
     */
    async completeOnboarding(accessToken, projectId, tierId, maxRetries = 10) {
        for (let i = 0; i < maxRetries; i++) {
            const result = await this.onboardUser(accessToken, projectId, tierId);
            if (result.done === true) {
                // Extract final project ID from response
                let finalProjectId = projectId;
                if (result.response?.cloudaicompanionProject) {
                    const respProject = result.response.cloudaicompanionProject;
                    if (typeof respProject === 'string') {
                        finalProjectId = respProject.trim();
                    }
                    else if (respProject.id) {
                        finalProjectId = respProject.id.trim();
                    }
                }
                return { success: true, projectId: finalProjectId };
            }
            // Wait 5 seconds before retry
            await new Promise(resolve => setTimeout(resolve, 5000));
        }
        throw new Error("Onboarding timeout - please try again");
    }
    /**
     * Fetch Project ID from loadCodeAssist API (legacy method for compatibility)
     */
    async fetchProjectId(accessToken) {
        const { projectId } = await this.loadCodeAssist(accessToken);
        if (!projectId) {
            throw new Error("No cloudaicompanionProject found in response");
        }
        return projectId;
    }
    /**
     * Save Antigravity 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,
        };
    }
    /**
     * Complete Antigravity OAuth flow
     */
    async connect() {
        const spinner = (0, ui_1.createSpinner)("Starting Antigravity 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 Antigravity 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 Antigravity 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("Loading Code Assist configuration...");
            // Load Code Assist to get project ID and tier
            const { projectId, tierId } = await this.loadCodeAssist(tokens.access_token);
            if (!projectId) {
                throw new Error("No Google Cloud Project found. Please ensure you have a GCP project with Gemini Code Assist enabled.");
            }
            spinner.updateText("Onboarding to Gemini Code Assist...");
            // Complete onboarding to enable Gemini Code Assist
            const onboardResult = await this.completeOnboarding(tokens.access_token, projectId, tierId);
            const finalProjectId = onboardResult.projectId || projectId;
            spinner.updateText("Saving tokens...");
            // Save tokens
            await this.saveTokens(tokens, userInfo, finalProjectId);
            spinner.succeed(`Antigravity connected successfully! (${userInfo.email}, Project: ${finalProjectId})`);
            return true;
        }
        catch (error) {
            spinner.fail(`Failed: ${error.message}`);
            throw error;
        }
    }
}
exports.AntigravityService = AntigravityService;
//# sourceMappingURL=antigravity.js.map