import enquirer from 'enquirer';
import { getDefaultConfigPath, getAuth, saveConfig, loadConfig, saveOrgId } from './config';
import { getResourceInfo } from './cache';
import { getCommand } from './command-prefix';
import * as tui from './tui';
import { defaultProfileName } from './config';
import { listOrganizations } from '@agentuity/server';
import { APIClient, getAPIBaseURL, getAppBaseURL } from './api';
export function isTTY() {
    return process.stdin.isTTY === true && process.stdout.isTTY === true;
}
const ORG_ID_ENV_VAR = 'AGENTUITY_CLOUD_ORG_ID';
const NON_INTERACTIVE_ORG_ERROR = 'Cannot select organization in non-interactive mode. ' +
    `Use --org-id, set ${ORG_ID_ENV_VAR}, or run interactively.`;
const RESOURCE_PREFIXES = [
    { prefix: 'sbx_', type: 'sandbox' },
    { prefix: 'proj_', type: 'project' },
    { prefix: 'db_', type: 'db' },
    { prefix: 'deploy_', type: 'deployment' },
    { prefix: 'machine_', type: 'machine' },
    { prefix: 'que_', type: 'queue' },
    { prefix: 'vec_', type: 'vector' },
    { prefix: 'kv_', type: 'kv' },
    { prefix: 'stream_', type: 'stream' },
    { prefix: 'eaddr_', type: 'email' },
    { prefix: 'edest_', type: 'email' },
    { prefix: 'ein_', type: 'email' },
    { prefix: 'eout_', type: 'email' },
    { prefix: 'edlv_', type: 'email' },
    { prefix: 'wh_', type: 'webhook' },
    { prefix: 'task_', type: 'task' },
];
function getResourceTypeFromId(id) {
    for (const entry of RESOURCE_PREFIXES) {
        if (id.startsWith(entry.prefix)) {
            return entry.type;
        }
    }
    return undefined;
}
function collectPrefixedIds(values) {
    if (!values) {
        return [];
    }
    const ids = new Set();
    const addValue = (value) => {
        if (typeof value === 'string') {
            const resourceType = getResourceTypeFromId(value);
            if (resourceType) {
                ids.add(value);
            }
            return;
        }
        if (Array.isArray(value)) {
            for (const entry of value) {
                if (typeof entry === 'string') {
                    const resourceType = getResourceTypeFromId(entry);
                    if (resourceType) {
                        ids.add(entry);
                    }
                }
            }
        }
    };
    for (const value of Object.values(values)) {
        addValue(value);
    }
    return Array.from(ids);
}
export function hasPrefixedResourceId(args, opts) {
    return collectPrefixedIds(args).length > 0 || collectPrefixedIds(opts).length > 0;
}
async function resolveCachedOrgId(ctx) {
    const profileName = ctx.config?.name ?? defaultProfileName;
    const candidateIds = [...collectPrefixedIds(ctx.args), ...collectPrefixedIds(ctx.opts)];
    for (const candidateId of candidateIds) {
        const resourceType = getResourceTypeFromId(candidateId);
        if (!resourceType) {
            continue;
        }
        const cachedInfo = await getResourceInfo(resourceType, profileName, candidateId);
        if (cachedInfo?.orgId) {
            return cachedInfo.orgId;
        }
    }
    return undefined;
}
export async function resolveOrgIdWithoutPrompt(ctx) {
    const { options, config, args, opts } = ctx;
    const envOrgId = process.env[ORG_ID_ENV_VAR];
    // Handle --org flag: could be true (use default), false, or a string org ID
    let flagOrgId;
    if (options.orgId === true) {
        // --org without argument means "use default org" - will fall through to preference/env
        flagOrgId = undefined;
    }
    else if (typeof options.orgId === 'string' && options.orgId !== envOrgId) {
        flagOrgId = options.orgId;
    }
    else {
        flagOrgId = undefined;
    }
    if (flagOrgId) {
        return flagOrgId;
    }
    if (envOrgId) {
        return envOrgId;
    }
    const preferredOrgId = config?.preferences?.orgId;
    if (preferredOrgId) {
        return preferredOrgId;
    }
    return resolveCachedOrgId({ config, args, opts });
}
export async function hasLoggedInBefore() {
    const configPath = getDefaultConfigPath();
    return await Bun.file(configPath).exists();
}
export async function isAuthenticated() {
    const auth = await getAuth();
    if (!auth) {
        return false;
    }
    return auth.expires > new Date();
}
export async function requireAuth(ctx) {
    const { logger } = ctx;
    const auth = await getAuth();
    if (auth && auth.expires > new Date()) {
        return auth;
    }
    const loginCmd = getCommand('auth login');
    const hasConfig = await hasLoggedInBefore();
    if (!isTTY()) {
        if (hasConfig) {
            logger.fatal(`You are not currently logged in or your session has expired.\n` +
                `Use "${loginCmd}" to login to Agentuity`);
        }
        else {
            logger.fatal(`Authentication required.\n` + `Use "${loginCmd}" to create an account or login`);
        }
    }
    // Show signup benefits box
    tui.showSignupBenefits();
    // Interactive mode - show warning and confirm
    tui.warning(hasConfig
        ? 'You are not currently logged in or your session has expired.'
        : 'Authentication required to continue.');
    tui.newline();
    const shouldLogin = await tui.confirm(hasConfig ? 'Would you like to login now?' : 'Would you like to create an account or login?', true);
    if (!shouldLogin) {
        return tui.fatal(`Authentication required. Run "${loginCmd}" when you're ready to continue.`);
    }
    tui.newline();
    // Import and run login flow
    const { loginCommand } = await import('./cmd/auth/login');
    // Ensure apiClient and opts are available for login handler
    const loginCtx = ctx;
    if (!loginCtx.apiClient) {
        const apiUrl = getAPIBaseURL(ctx.config ?? null);
        loginCtx.apiClient = new APIClient(apiUrl, ctx.logger, ctx.config ?? null);
    }
    loginCtx.opts ??= {};
    if (loginCommand.handler) {
        await loginCommand.handler(loginCtx);
    }
    // After login completes, verify we have auth
    const newAuth = await getAuth();
    if (!newAuth || newAuth.expires <= new Date()) {
        return tui.fatal('Login was not completed successfully.');
    }
    tui.newline();
    return newAuth;
}
export async function optionalAuth(ctx, continueText, skipPrompts) {
    const auth = await getAuth();
    if (auth && auth.expires > new Date()) {
        return auth;
    }
    // Skip interactive prompts if requested (e.g., --confirm flag, --no-register in CI/scripts)
    // Still show the logged out message to inform user about limited capabilities
    if (skipPrompts) {
        if (isTTY()) {
            const hasLoggedIn = await hasLoggedInBefore();
            tui.showLoggedOutMessage(getAppBaseURL(ctx.config ?? null), hasLoggedIn);
        }
        return null;
    }
    // Show signup benefits but don't block - just return null
    if (isTTY()) {
        const config = await loadConfig();
        // check to see if we've shown the banner or logged in before
        const benefitsShown = config?.preferences?.signup_banner_shown === true;
        const hasLoggedIn = await hasLoggedInBefore();
        // if we haven't shown it, show it once and then remember that we've shown it
        if (!benefitsShown && hasLoggedIn) {
            tui.showSignupBenefits();
            if (!config) {
                ctx.config = { name: defaultProfileName };
            }
            else {
                ctx.config = config;
            }
            if (!ctx.config.preferences) {
                ctx.config.preferences = {};
            }
            ctx.config.preferences.signup_banner_shown = true;
            await saveConfig(ctx.config);
            // Show select menu with custom or default text
            const defaultContinueText = 'Start without an account (run locally)';
            const response = await enquirer.prompt({
                type: 'select',
                name: 'action',
                message: 'How would you like to continue?',
                choices: [
                    {
                        name: 'login',
                        message: 'Create an account or login',
                    },
                    {
                        name: 'local',
                        message: continueText || defaultContinueText,
                    },
                ],
            });
            if (response.action === 'local') {
                tui.showLoggedOutMessage(getAppBaseURL(ctx.config ?? null), hasLoggedIn);
                return null;
            }
            tui.newline();
            // Import and run login flow
            const { loginCommand } = await import('./cmd/auth/login');
            // Ensure apiClient and opts are available for login handler
            const loginCtx1 = ctx;
            if (!loginCtx1.apiClient) {
                const apiUrl = getAPIBaseURL(ctx.config ?? null);
                loginCtx1.apiClient = new APIClient(apiUrl, ctx.logger, ctx.config ?? null);
            }
            loginCtx1.opts ??= {};
            if (loginCommand.handler) {
                await loginCommand.handler(loginCtx1);
            }
            return getAuth();
        }
        if (hasLoggedIn) {
            tui.warning('You are not currently logged in');
            tui.newline();
            const response = await enquirer.prompt({
                type: 'select',
                name: 'action',
                message: 'How would you like to continue?',
                choices: [
                    {
                        name: 'local',
                        message: 'Continue without login',
                    },
                    {
                        name: 'login',
                        message: 'Login',
                    },
                ],
            });
            if (response.action === 'local') {
                tui.showLoggedOutMessage(getAppBaseURL(ctx.config ?? null), hasLoggedIn);
                return null;
            }
            tui.newline();
            // Import and run login flow
            const { loginCommand } = await import('./cmd/auth/login');
            // Ensure apiClient and opts are available for login handler
            const loginCtx2 = ctx;
            if (!loginCtx2.apiClient) {
                const apiUrl = getAPIBaseURL(ctx.config ?? null);
                loginCtx2.apiClient = new APIClient(apiUrl, ctx.logger, ctx.config ?? null);
            }
            loginCtx2.opts ??= {};
            if (loginCommand.handler) {
                await loginCommand.handler(loginCtx2);
            }
            return getAuth();
        }
    }
    return null;
}
export async function requireOrg(ctx, autoSelect) {
    const { options, config, apiClient, args, opts } = ctx;
    const interactive = isTTY();
    const envOrgId = process.env[ORG_ID_ENV_VAR];
    const flagOrgId = options.orgId && options.orgId !== envOrgId ? options.orgId : undefined;
    // 1. --org-id flag
    if (flagOrgId) {
        return flagOrgId;
    }
    // 2. Environment variable
    if (envOrgId) {
        return envOrgId;
    }
    // 3. Config preference (TTY: prompt with pre-selected, non-TTY: use directly)
    const preferredOrgId = config?.preferences?.orgId;
    if (preferredOrgId && (!interactive || autoSelect)) {
        return preferredOrgId;
    }
    // Cache lookup for prefixed resource IDs
    if (!preferredOrgId) {
        const cachedOrgId = await resolveCachedOrgId({ config, args, opts });
        if (cachedOrgId) {
            return cachedOrgId;
        }
    }
    // Fetch organizations
    const orgs = await tui.spinner({
        message: 'Fetching organizations',
        clearOnSuccess: true,
        callback: async () => {
            return listOrganizations(apiClient);
        },
    });
    if (orgs.length === 0) {
        tui.fatal('No organizations found for your account');
    }
    // 4. Single-org auto-select
    if (orgs.length === 1 && orgs[0]) {
        const orgId = orgs[0].id;
        if (orgId !== preferredOrgId) {
            await saveOrgId(orgId);
        }
        return orgId;
    }
    // Auto-select mode (--confirm flag or explicit autoSelect)
    if (autoSelect) {
        const orgId = await tui.selectOrganization(orgs, preferredOrgId, true);
        if (orgId !== preferredOrgId) {
            await saveOrgId(orgId);
        }
        return orgId;
    }
    // 5. Interactive prompt (TTY) / Error (no TTY)
    if (!interactive) {
        return tui.fatal(NON_INTERACTIVE_ORG_ERROR);
    }
    const orgId = await tui.selectOrganization(orgs, preferredOrgId, false);
    // Save selected org to config if different
    if (orgId !== preferredOrgId) {
        await saveOrgId(orgId);
    }
    return orgId;
}
export async function optionalOrg(ctx, autoSelect) {
    const { options, config, apiClient, auth, args, opts } = ctx;
    const interactive = isTTY();
    const envOrgId = process.env[ORG_ID_ENV_VAR];
    const flagOrgId = options.orgId && options.orgId !== envOrgId ? options.orgId : undefined;
    // If not authenticated or no API client, skip org selection
    if (!auth || !apiClient) {
        return undefined;
    }
    // If auth exists but has no API key, skip (likely unauthenticated or test scenario)
    if (!auth.apiKey) {
        return undefined;
    }
    // Note: --no-register check is handled in cli.ts before calling this function
    // 1. --org-id flag
    if (flagOrgId) {
        return flagOrgId;
    }
    // 2. Environment variable
    if (envOrgId) {
        return envOrgId;
    }
    // 3. Config preference (TTY: prompt with pre-selected, non-TTY: use directly)
    const preferredOrgId = config?.preferences?.orgId;
    if (preferredOrgId && (!interactive || autoSelect)) {
        return preferredOrgId;
    }
    // Cache lookup for prefixed resource IDs
    if (!preferredOrgId) {
        const cachedOrgId = await resolveCachedOrgId({ config, args, opts });
        if (cachedOrgId) {
            return cachedOrgId;
        }
    }
    // Fetch organizations
    const orgs = await tui.spinner({
        message: 'Fetching organizations',
        clearOnSuccess: true,
        callback: async () => {
            return listOrganizations(apiClient);
        },
    });
    if (orgs.length === 0) {
        return undefined;
    }
    // 4. Single-org auto-select
    if (orgs.length === 1 && orgs[0]) {
        const orgId = orgs[0].id;
        if (orgId !== preferredOrgId) {
            await saveOrgId(orgId);
        }
        return orgId;
    }
    // Auto-select mode (--confirm flag or explicit autoSelect)
    if (autoSelect) {
        const orgId = await tui.selectOrganization(orgs, preferredOrgId, true);
        if (orgId !== preferredOrgId) {
            await saveOrgId(orgId);
        }
        return orgId;
    }
    // 5. Interactive prompt (TTY) / Error (no TTY)
    if (!interactive) {
        return tui.fatal(NON_INTERACTIVE_ORG_ERROR);
    }
    // Prompt for org selection (use saved preference as initial/default)
    const orgId = await tui.selectOrganization(orgs, preferredOrgId, false);
    // Save selected org to config if different
    if (orgId !== preferredOrgId) {
        await saveOrgId(orgId);
    }
    return orgId;
}
//# sourceMappingURL=auth.js.map