"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setOpenBrowserForTests = exports.resetOpenBrowserForTests = void 0;
exports.login = login;
const types_1 = require("@playdrop/types");
const config_1 = require("../config");
const apiClient_1 = require("../apiClient");
const http_1 = require("../http");
const environment_1 = require("../environment");
const browser_1 = require("../browser");
Object.defineProperty(exports, "resetOpenBrowserForTests", { enumerable: true, get: function () { return browser_1.resetOpenBrowserForTests; } });
Object.defineProperty(exports, "setOpenBrowserForTests", { enumerable: true, get: function () { return browser_1.setOpenBrowserForTests; } });
const messages_1 = require("../messages");
function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}
function extractAccessToken(data) {
    return data.accessToken
        ?? (typeof data === 'object' && data !== null && 'token' in data ? data.token : undefined);
}
function isLoginResponse(data) {
    return typeof data === 'object' && data !== null && 'user' in data;
}
function storeLogin(env, data) {
    const token = extractAccessToken(data);
    if (!token) {
        (0, messages_1.printErrorWithHelp)('Login succeeded but no access token was returned.', [
            'Please try again in a moment.',
            'If the issue persists, contact the Playdrop team.',
        ], { command: 'login' });
        process.exitCode = 1;
        return;
    }
    const username = typeof data.user?.username === 'string' ? data.user.username.trim() : '';
    if (!username) {
        (0, messages_1.printErrorWithHelp)('Login succeeded but the account username was missing.', [
            'Please try again in a moment.',
            'If the issue persists, contact the Playdrop team.',
        ], { command: 'login' });
        process.exitCode = 1;
        return;
    }
    (0, config_1.saveAccountSession)({ username, env, token });
    console.log(`Logged in as ${username} on ${env}.`);
    console.log('Next: run "playdrop auth whoami" to confirm your session.');
}
async function loginWithUsernamePassword(env, baseUrl, username, password) {
    const client = (0, apiClient_1.createCliApiClient)({ baseUrl });
    try {
        const data = await client.login({ username, password });
        storeLogin(env, data);
    }
    catch (unknownError) {
        if (unknownError instanceof types_1.UnsupportedClientError) {
            (0, http_1.handleUnsupportedError)(unknownError, 'Login');
            return;
        }
        if (unknownError instanceof types_1.ApiError) {
            (0, messages_1.printErrorWithHelp)(`Login failed with status ${unknownError.status}.`, [
                'Verify your username and password, then rerun "playdrop auth login".',
                'Use "playdrop auth whoami" after logging in to confirm your status.',
            ], { command: 'login' });
            process.exitCode = 1;
            return;
        }
        throw unknownError;
    }
}
async function loginWithKey(env, baseUrl, apiKey) {
    const client = (0, apiClient_1.createCliApiClient)({ baseUrl });
    try {
        const data = await client.loginWithApiKey({ apiKey });
        storeLogin(env, data);
    }
    catch (unknownError) {
        if (unknownError instanceof types_1.UnsupportedClientError) {
            (0, http_1.handleUnsupportedError)(unknownError, 'Login');
            return;
        }
        if (unknownError instanceof types_1.ApiError) {
            (0, messages_1.printErrorWithHelp)(`Login failed with status ${unknownError.status}.`, [
                'Verify your API key, then rerun "playdrop auth login --key <api-key>".',
                'Use "playdrop auth whoami" after logging in to confirm your status.',
            ], { command: 'login' });
            process.exitCode = 1;
            return;
        }
        throw unknownError;
    }
}
async function loginInBrowser(env, baseUrl) {
    const client = (0, apiClient_1.createCliApiClient)({ baseUrl });
    let flow;
    try {
        flow = await client.startCliLogin();
    }
    catch (unknownError) {
        if (unknownError instanceof types_1.UnsupportedClientError) {
            (0, http_1.handleUnsupportedError)(unknownError, 'Login');
            return;
        }
        if (unknownError instanceof types_1.ApiError) {
            (0, messages_1.printErrorWithHelp)(`Could not start browser login (status ${unknownError.status}).`, [
                'Try again in a moment.',
                'If the problem continues, contact the Playdrop team.',
            ], { command: 'login' });
            process.exitCode = 1;
            return;
        }
        throw unknownError;
    }
    console.log(`Open this URL to continue: ${flow.loginUrl}`);
    const opened = await (0, browser_1.openBrowserUrl)(flow.loginUrl).catch(() => false);
    if (opened) {
        console.log('Opened the login page in your browser.');
    }
    else {
        console.log('Open the URL above in your browser to continue.');
    }
    const expiresAtMs = Date.parse(flow.expiresAt);
    const deadlineMs = Number.isFinite(expiresAtMs)
        ? expiresAtMs
        : Date.now() + 10 * 60 * 1000;
    const intervalMs = Math.max(250, Number(flow.pollIntervalMs) || 1000);
    while (Date.now() < deadlineMs) {
        try {
            const result = await client.pollCliLogin({
                requestId: flow.requestId,
                pollToken: flow.pollToken,
            });
            if (!isLoginResponse(result)) {
                await sleep(intervalMs);
                continue;
            }
            storeLogin(env, result);
            return;
        }
        catch (unknownError) {
            if (unknownError instanceof types_1.UnsupportedClientError) {
                (0, http_1.handleUnsupportedError)(unknownError, 'Login');
                return;
            }
            if (unknownError instanceof types_1.ApiError) {
                if (unknownError.status === 410 || unknownError.code === 'cli_login_expired') {
                    (0, messages_1.printErrorWithHelp)('Browser login expired before it was approved.', [
                        'Rerun "playdrop auth login" to start a new browser login request.',
                    ], { command: 'login' });
                    process.exitCode = 1;
                    return;
                }
                (0, messages_1.printErrorWithHelp)(`Browser login failed with status ${unknownError.status}.`, [
                    'Rerun "playdrop auth login" to try again.',
                ], { command: 'login' });
                process.exitCode = 1;
                return;
            }
            throw unknownError;
        }
    }
    (0, messages_1.printErrorWithHelp)('Browser login timed out after 10 minutes.', [
        'Rerun "playdrop auth login" to start a new browser login request.',
    ], { command: 'login' });
    process.exitCode = 1;
}
async function login(env, options = {}) {
    const envConfig = (0, environment_1.resolveEnvironmentConfig)(env);
    if (!envConfig) {
        const choices = (0, environment_1.formatEnvironmentList)();
        (0, messages_1.printUnknownEnvironment)(env, choices, 'login');
        process.exitCode = 1;
        return;
    }
    if (envConfig.allowInsecureRequests) {
        process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
    }
    const username = options.username?.trim();
    const password = options.password;
    const apiKey = options.key?.trim();
    if (apiKey && (username || password)) {
        (0, messages_1.printErrorWithHelp)('Choose exactly one login method.', [
            'Use "playdrop auth login" for browser login.',
            'Use "--username" with "--password" for direct login.',
            'Use "--key" by itself for API key login.',
        ], { command: 'login' });
        process.exitCode = 1;
        return;
    }
    if ((username && !password) || (!username && password)) {
        (0, messages_1.printErrorWithHelp)('Provide both "--username" and "--password" together.', [
            'Rerun "playdrop auth login --username <username> --password <password>".',
            'Or run "playdrop auth login" to use browser login instead.',
        ], { command: 'login' });
        process.exitCode = 1;
        return;
    }
    try {
        if (apiKey) {
            await loginWithKey(env, envConfig.apiBase, apiKey);
            return;
        }
        if (username && password) {
            await loginWithUsernamePassword(env, envConfig.apiBase, username, password);
            return;
        }
        await loginInBrowser(env, envConfig.apiBase);
    }
    catch (error) {
        if (error instanceof http_1.CLIUnsupportedClientError) {
            return;
        }
        (0, messages_1.printNetworkIssue)('Could not reach the Playdrop API to log in.', 'login');
        process.exitCode = 1;
    }
}
