import opn from "open";
import { captureError } from "../common/analytics/lib.js";
import { logger } from "../common/logger.js";
import { printCriticalFailureToConsoleAndExit } from "../common/output.js";
import settings from "../common/settings.js";
import box from "../common/utils/box.js";
import { saveAuthState } from "./tokens.js";
export const CLIENT_ID = settings.AUTH0_CLIENT_ID;
export const AUTH0_DOMAIN = settings.AUTH0_DOMAIN;
export const OAUTH_SCOPE = "offline_access";
export const OAUTH_AUDIENCE = "https://dev.zuplo.com/";
class OAuthError extends Error {
}
async function initiateDeviceFlow() {
    const response = await fetch(`https://${AUTH0_DOMAIN}/oauth/device/code`, {
        method: "POST",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
        },
        body: new URLSearchParams({
            client_id: CLIENT_ID,
            scope: OAUTH_SCOPE,
            audience: OAUTH_AUDIENCE,
        }),
    });
    if (!response.ok) {
        let errorDetails = {};
        try {
            errorDetails = await response.json();
        }
        catch {
        }
        const errorMessage = errorDetails.error_description ||
            errorDetails.error ||
            response.statusText;
        await captureError(new Error(`Failed to initiate device flow: ${errorMessage}`), {
            oauth_error: errorDetails.error || "device_code_failed",
            source: "cli_device_flow_init",
            status_code: response.status,
            error_description: errorDetails.error_description,
            error_details: errorDetails,
            status: response.status,
            status_text: response.statusText,
        });
        throw new Error(`Failed to initiate device flow: ${errorMessage}`);
    }
    return response.json();
}
async function pollForToken(deviceCode, interval, expiresIn) {
    const startTime = Date.now();
    const expirationTime = startTime + expiresIn * 1000;
    while (Date.now() < expirationTime) {
        await new Promise((resolve) => setTimeout(resolve, interval * 1000));
        const response = await fetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
            },
            body: new URLSearchParams({
                grant_type: "urn:ietf:params:oauth:grant-type:device_code",
                device_code: deviceCode,
                client_id: CLIENT_ID,
            }),
        });
        const result = await response.json();
        if (result.error) {
            if (result.error === "authorization_pending") {
                logger.debug("Authorization pending, continuing to poll...");
                continue;
            }
            else if (result.error === "slow_down") {
                interval = interval + 5;
                logger.debug(`Slowing down polling interval to ${interval} seconds`);
                continue;
            }
            else if (result.error === "expired_token") {
                await captureError(new Error("Device code expired before user authorization"), {
                    oauth_error: "expired_token",
                    source: "cli_device_flow_poll",
                });
                throw new OAuthError("The device code has expired.");
            }
            else if (result.error === "access_denied") {
                await captureError(new Error("User denied authorization"), {
                    oauth_error: "access_denied",
                    source: "cli_device_flow_poll",
                });
                throw new OAuthError("Authorization was denied.");
            }
            else {
                await captureError(new Error(`Device flow error: ${result.error}`), {
                    oauth_error: result.error,
                    source: "cli_device_flow_poll",
                    error_description: result.error_description,
                });
                throw new OAuthError(result.error_description || `OAuth error: ${result.error}`);
            }
        }
        if (result.access_token) {
            return result;
        }
    }
    await captureError(new Error("Device flow timed out waiting for user authorization"), {
        oauth_error: "timeout",
        source: "cli_device_flow_poll",
    });
    throw new OAuthError("Timed out waiting for authorization.");
}
export async function login() {
    try {
        const deviceResponse = await initiateDeviceFlow();
        console.log(box(` To login, please visit:

${deviceResponse.verification_uri_complete}

After completing login in the browser you can close the tab.`, {
            borderColor: "white",
        }));
        try {
            await opn(deviceResponse.verification_uri_complete);
        }
        catch {
        }
        let tokenResponse;
        try {
            tokenResponse = await pollForToken(deviceResponse.device_code, deviceResponse.interval, deviceResponse.expires_in);
        }
        catch (err) {
            if (err instanceof OAuthError) {
                return printCriticalFailureToConsoleAndExit(`${err.message}. Please run the login command again.`);
            }
            else {
                return printCriticalFailureToConsoleAndExit("An error occurred while attempting to authenticate. Please run the login command again. If the problem persists, contact support@zuplo.com.");
            }
        }
        await saveAuthState(tokenResponse);
    }
    catch (error) {
        logger.error(error, "Login failed");
        throw error;
    }
}
//# sourceMappingURL=login.js.map