/** Interactive-shell auth workflow for login, token storage, and session-bound auth checks. */
import { getOAuthProviders } from '../../ai/utils/oauth/index.js';
import { LoginRequiredError } from '../../app/agent-session.js';
import { fetchUserInfo } from '../../app/services/user-context.js';
import { getAuthPath } from '../../config.js';
import { LoginDialogComponent } from '../components/selectors/login-dialog.js';
import { OAuthSelectorComponent } from '../components/selectors/oauth-selector.js';
export class AuthWorkflow {
    deps;
    createOAuthSelectorImpl;
    createLoginDialogImpl;
    getOAuthProvidersImpl;
    constructor(deps) {
        this.deps = deps;
        this.createOAuthSelectorImpl =
            deps.createOAuthSelector ??
                ((args) => {
                    const selector = new OAuthSelectorComponent({
                        mode: args.mode,
                        // OAuthSelectorComponent expects the concrete AuthStorage; the workflow
                        // takes the narrower ShellAuthStorage surface, so cast at the boundary.
                        authStorage: args.authStorage,
                        providerOrder: args.providerOrder,
                        onSelect: args.onSelect,
                        onCancel: args.onCancel
                    });
                    return { component: selector, focus: selector };
                });
        this.createLoginDialogImpl =
            deps.createLoginDialog ??
                ((providerId) => new LoginDialogComponent(deps.ui, providerId, () => { }));
        this.getOAuthProvidersImpl = deps.getOAuthProviders ?? getOAuthProviders;
    }
    async showOAuthSelector(mode) {
        if (mode === 'logout') {
            const providers = this.deps.session.modelRegistry.authStorage.list();
            const loggedInProviders = providers.filter((providerId) => this.deps.session.modelRegistry.authStorage.get(providerId)?.type === 'oauth');
            if (loggedInProviders.length === 0) {
                this.deps.showStatus('No OAuth providers logged in. Use /login first.');
                return;
            }
        }
        this.deps.showSelector((done) => this.createOAuthSelectorImpl({
            mode,
            authStorage: this.deps.session.modelRegistry.authStorage,
            providerOrder: this.deps.oauthProviderOrder,
            onSelect: async (providerId) => {
                done();
                if (mode === 'login') {
                    await this.showLoginDialog(providerId);
                    return;
                }
                await this.logoutProvider(providerId);
            },
            onCancel: () => {
                done();
                this.deps.ui.requestRender();
            }
        }));
    }
    async ensureShortcutAuth() {
        const authStorage = this.deps.session.modelRegistry.authStorage;
        const providerId = 'shortcut';
        if (!authStorage.has(providerId)) {
            this.deps.showStatus('Welcome! Please log in to get started.');
            await this.showLoginDialog(providerId);
            return;
        }
        const apiKey = await authStorage.getApiKey(providerId);
        if (!apiKey) {
            this.deps.showStatus('Session expired. Logging in to Shortcut...');
            await this.showLoginDialog(providerId);
        }
    }
    async promptWithLoginRetry(fn) {
        try {
            await fn();
            return true;
        }
        catch (error) {
            if (error instanceof LoginRequiredError) {
                this.deps.showStatus('Logging in to Shortcut...');
                await this.showLoginDialog('shortcut');
                try {
                    await fn();
                    return true;
                }
                catch (retryError) {
                    this.deps.showError(retryError instanceof Error ? retryError.message : String(retryError));
                    return false;
                }
            }
            this.deps.showError(error instanceof Error ? error.message : String(error));
            return false;
        }
    }
    async showLoginDialog(providerId) {
        const providerInfo = this.getOAuthProvidersImpl().find((provider) => provider.id === providerId);
        const providerName = providerInfo?.name || providerId;
        const usesCallbackServer = providerInfo?.usesCallbackServer ?? false;
        const dialog = this.createLoginDialogImpl(providerId);
        this.deps.editorContainer.clear();
        this.deps.editorContainer.addChild(dialog);
        this.deps.ui.setFocus(dialog);
        this.deps.ui.requestRender();
        const restoreEditor = () => {
            this.deps.restoreEditorToContainer();
            this.deps.ui.setFocus(this.deps.getEditor());
            this.deps.ui.requestRender();
        };
        try {
            await this.deps.onProviderLoginStarted?.(providerId);
            await this.deps.session.modelRegistry.authStorage.login(providerId, {
                onAuth: (info) => {
                    dialog.showAuth(info.url, info.instructions);
                    if (usesCallbackServer) {
                        dialog.showWaiting('Waiting for browser authentication...');
                    }
                },
                onPrompt: async (prompt) => (await dialog.showPrompt(prompt.message, prompt.placeholder)) ?? '',
                onProgress: (message) => {
                    dialog.showProgress(message);
                },
                signal: dialog.signal
            });
            restoreEditor();
            this.deps.session.modelRegistry.refresh();
            await this.deps.updateAvailableProviderCount();
            await this.autoSwitchToProvider(providerId);
            await this.deps.refreshCreditBalance();
            const credential = this.deps.session.modelRegistry.authStorage.get(providerId);
            const userId = credential?.type === 'oauth' && credential.access
                ? (await fetchUserInfo(credential.access))?.userId
                : undefined;
            await this.deps.onProviderLoggedIn?.(providerId, userId);
            this.deps.showStatus(`Logged in to ${providerName}. Credentials saved to ${getAuthPath()}`);
        }
        catch (error) {
            restoreEditor();
            const message = error instanceof Error ? error.message : String(error);
            if (message !== 'Login cancelled') {
                await this.deps.onProviderLoginFailed?.(providerId, message);
                this.deps.showError(`Failed to login to ${providerName}: ${message}`);
            }
        }
    }
    async logoutProvider(providerId) {
        const providerInfo = this.getOAuthProvidersImpl().find((provider) => provider.id === providerId);
        const providerName = providerInfo?.name || providerId;
        try {
            this.deps.session.modelRegistry.authStorage.logout(providerId);
            this.deps.session.modelRegistry.refresh();
            await this.deps.updateAvailableProviderCount();
            await this.deps.onProviderLoggedOut?.(providerId);
            this.deps.showStatus(`Logged out of ${providerName}`);
        }
        catch (error) {
            this.deps.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    async autoSwitchToProvider(providerId) {
        const available = this.deps.session.modelRegistry.getAvailable();
        const providerModels = available.filter((model) => model.provider === providerId);
        if (providerModels.length === 0) {
            return;
        }
        const currentModel = this.deps.session.model;
        if (!currentModel || currentModel.provider !== providerId) {
            try {
                await this.deps.session.setModel(providerModels[0]);
                this.deps.onModelChanged();
            }
            catch {
                // Keep the existing model if the switch fails; user can fix it manually.
            }
        }
    }
}
//# sourceMappingURL=auth-workflow.js.map