"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MainThreadAuthentication = exports.MainThreadAuthenticationProvider = void 0;
const tslib_1 = require("tslib");
const di_1 = require("@opensumi/di");
const ide_connection_1 = require("@opensumi/ide-connection");
const ide_core_browser_1 = require("@opensumi/ide-core-browser");
const ide_core_common_1 = require("@opensumi/ide-core-common");
const ide_overlay_1 = require("@opensumi/ide-overlay");
const vscode_1 = require("../../../common/vscode");
const types_1 = require("../../types");
let MainThreadAuthenticationProvider = class MainThreadAuthenticationProvider extends ide_core_browser_1.Disposable {
    constructor(_proxy, id, label, supportsMultipleAccounts) {
        super();
        this._proxy = _proxy;
        this.id = id;
        this.label = label;
        this.supportsMultipleAccounts = supportsMultipleAccounts;
        this._accounts = new Map(); // Map account name to session ids
        this._sessions = new Map(); // Map account id to name
    }
    async initialize() {
        return this.registerCommandsAndContextMenuItems();
    }
    async manageTrustedExtensions(accountName) {
        const allowedExtensions = await this.authenticationService.getAllowedExtensions(this.id, accountName);
        if (!allowedExtensions.length) {
            this.dialogService.info((0, ide_core_browser_1.localize)('authentication.noTrustedExtensions'));
            return;
        }
        const usages = await this.authenticationService.getAccountUsages(this.id, accountName);
        const items = allowedExtensions.map((extension) => {
            const usage = usages.find((usage) => extension.id === usage.extensionId);
            return {
                label: extension.name,
                description: usage ? (0, ide_core_browser_1.localize)('authentication.accountLastUsedDate') : (0, ide_core_browser_1.localize)('authentication.notUsed'),
                value: extension,
            };
        });
        const trustedExtension = await this.quickPickService.show(items, {
            title: (0, ide_core_browser_1.localize)('authentication.manageTrustedExtensions'),
            placeholder: (0, ide_core_browser_1.localize)('authentication.manageExtensions'),
        });
        if (trustedExtension) {
            await this.authenticationService.setAllowedExtensions(this.id, accountName, [trustedExtension]);
        }
    }
    async signOut(accountName) {
        const accountUsages = await this.authenticationService.getAccountUsages(this.id, accountName);
        const sessionsForAccount = this._accounts.get(accountName);
        const message = accountUsages.length
            ? (0, ide_core_browser_1.formatLocalize)('authentication.signOutMessage', accountName, accountUsages.map((usage) => usage.extensionName).join('\n'))
            : (0, ide_core_browser_1.formatLocalize)('authentication.signOutMessageSimple', accountName);
        const result = await this.dialogService.info(message, [(0, ide_core_browser_1.localize)('ButtonCancel'), (0, ide_core_browser_1.localize)('ButtonOK')]);
        if (result === (0, ide_core_browser_1.localize)('ButtonOK')) {
            sessionsForAccount === null || sessionsForAccount === void 0 ? void 0 : sessionsForAccount.forEach((sessionId) => this.logout(sessionId));
            await this.authenticationService.removeAccountUsage(this.id, accountName);
            await this.authenticationService.removeAllowedExtensions(this.id, accountName);
        }
    }
    async getSessions(scopes, account) {
        return this._proxy.$getSessions(this.id, scopes, { account });
    }
    hasSessions() {
        return !!this._sessions.size;
    }
    async updateSessionItems(event) {
        const { added, removed } = event;
        const session = await this._proxy.$getSessions(this.id);
        const addedSessions = session.filter((session) => added.some((s) => s.id === session.id));
        removed.forEach((session) => {
            const accountName = this._sessions.get(session.id);
            if (accountName) {
                this._sessions.delete(session.id);
                const sessionsForAccount = this._accounts.get(accountName) || [];
                const sessionIndex = sessionsForAccount.indexOf(session.id);
                sessionsForAccount.splice(sessionIndex);
                if (!sessionsForAccount.length) {
                    this._accounts.delete(accountName);
                }
            }
        });
        addedSessions.forEach((session) => this.registerSession(session));
    }
    async registerCommandsAndContextMenuItems() {
        try {
            const sessions = await this._proxy.$getSessions(this.id);
            sessions.forEach((session) => this.registerSession(session));
        }
        catch (err) {
            this.logger.error(err);
        }
    }
    registerSession(session) {
        this._sessions.set(session.id, session.account.label);
        const existingSessionsForAccount = this._accounts.get(session.account.label);
        if (existingSessionsForAccount) {
            this._accounts.set(session.account.label, existingSessionsForAccount.concat(session.id));
            return;
        }
        else {
            this._accounts.set(session.account.label, [session.id]);
        }
    }
    login(scopes, options) {
        return this._proxy.$login(this.id, scopes, options);
    }
    async logout(sessionId) {
        await this._proxy.$logout(this.id, sessionId);
        this.messageService.info((0, ide_core_browser_1.localize)('authentication.signedOut'));
    }
};
exports.MainThreadAuthenticationProvider = MainThreadAuthenticationProvider;
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_common_1.IAuthenticationService),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthenticationProvider.prototype, "authenticationService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.QuickPickService),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthenticationProvider.prototype, "quickPickService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_overlay_1.IDialogService),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthenticationProvider.prototype, "dialogService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_overlay_1.IMessageService),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthenticationProvider.prototype, "messageService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.ILogger),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthenticationProvider.prototype, "logger", void 0);
exports.MainThreadAuthenticationProvider = MainThreadAuthenticationProvider = tslib_1.__decorate([
    (0, di_1.Injectable)({ multiple: true }),
    tslib_1.__metadata("design:paramtypes", [Object, String, String, Boolean])
], MainThreadAuthenticationProvider);
let MainThreadAuthentication = class MainThreadAuthentication extends ide_core_browser_1.Disposable {
    constructor(protocol) {
        super();
        this._proxy = protocol.getProxy(vscode_1.ExtHostAPIIdentifier.ExtHostAuthentication);
        this.addDispose(this.authenticationService.onDidChangeSessions((e) => {
            this._proxy.$onDidChangeAuthenticationSessions(e.providerId, e.label, e.event);
        }));
        this.addDispose(this.authenticationService.onDidRegisterAuthenticationProvider((info) => {
            this._proxy.$onDidChangeAuthenticationProviders([info], []);
        }));
        this.addDispose(this.authenticationService.onDidUnregisterAuthenticationProvider((info) => {
            this._proxy.$onDidChangeAuthenticationProviders([], [info]);
        }));
    }
    $getProviderIds() {
        return Promise.resolve(this.authenticationService.getProviderIds());
    }
    async $registerAuthenticationProvider(id, label, supportsMultipleAccounts) {
        const provider = this.injector.get(MainThreadAuthenticationProvider, [
            this._proxy,
            id,
            label,
            supportsMultipleAccounts,
        ]);
        await provider.initialize();
        this.authenticationService.registerAuthenticationProvider(id, provider);
        this.addDispose(ide_core_browser_1.Disposable.create(() => {
            this.$unregisterAuthenticationProvider(id);
        }));
    }
    $unregisterAuthenticationProvider(id) {
        this.authenticationService.unregisterAuthenticationProvider(id);
    }
    $ensureProvider(id) {
        return this.activationService.fireEvent('onAuthenticationRequest', id);
    }
    $sendDidChangeSessions(id, event) {
        this.authenticationService.sessionsUpdate(id, event);
    }
    $getSessions(id) {
        return this.authenticationService.getSessions(id);
    }
    $login(providerId, scopes, options) {
        return this.authenticationService.login(providerId, scopes, options);
    }
    $logout(providerId, sessionId) {
        return this.authenticationService.logout(providerId, sessionId);
    }
    $getAccounts(providerId) {
        return this.authenticationService
            .getSessions(providerId)
            .then((sessions) => sessions.map((session) => session.account));
    }
    async doGetSession(providerId, scopes, extensionId, extensionName, options) {
        const sessions = await this.authenticationService.getSessions(providerId, scopes, true);
        const supportsMultipleAccounts = this.authenticationService.supportsMultipleAccounts(providerId);
        // Error cases
        if (options.forceNewSession && !sessions.length) {
            throw new Error('No existing sessions found.');
        }
        if (options.forceNewSession && options.createIfNone) {
            throw new Error('Invalid combination of options. Please remove one of the following: forceNewSession, createIfNone');
        }
        if (options.forceNewSession && options.silent) {
            throw new Error('Invalid combination of options. Please remove one of the following: forceNewSession, silent');
        }
        if (options.createIfNone && options.silent) {
            throw new Error('Invalid combination of options. Please remove one of the following: createIfNone, silent');
        }
        // Check if the sessions we have are valid
        if (!options.forceNewSession && sessions.length) {
            if (supportsMultipleAccounts) {
                if (options.clearSessionPreference) {
                    await this.authenticationService.removeExtensionSessionId(extensionName, providerId);
                }
                else {
                    const existingSessionPreference = await this.authenticationService.getExtensionSessionId(extensionName, providerId);
                    if (existingSessionPreference) {
                        const matchingSession = sessions.find((session) => session.id === existingSessionPreference);
                        if (matchingSession &&
                            (await this.authenticationService.isAccessAllowed(providerId, matchingSession.account.label, extensionId))) {
                            return matchingSession;
                        }
                    }
                }
            }
            else if (await this.authenticationService.isAccessAllowed(providerId, sessions[0].account.label, extensionId)) {
                return sessions[0];
            }
        }
        // We may need to prompt because we don't have a valid session
        // modal flows
        if (options.createIfNone || options.forceNewSession) {
            const providerName = this.authenticationService.getLabel(providerId);
            const detail = typeof options.forceNewSession === 'object' ? options.forceNewSession.detail : undefined;
            const isAllowed = await this.loginPrompt(providerName, extensionName, !!options.forceNewSession, detail);
            if (!isAllowed) {
                throw new Error('User did not consent to login.');
            }
            const session = (sessions === null || sessions === void 0 ? void 0 : sessions.length) && !options.forceNewSession && supportsMultipleAccounts
                ? await this.selectSession(providerId, providerName, extensionId, extensionName, sessions, scopes, options, !!options.clearSessionPreference)
                : await this.authenticationService.login(providerId, scopes, options);
            await this.authenticationService.updatedAllowedExtension(providerId, session.account.label, extensionId, extensionName, true);
            await this.authenticationService.setExtensionSessionId(extensionName, providerId, session.id);
            return session;
        }
        // passive flows (silent or default)
        const validSession = sessions.find((s) => this.authenticationService.isAccessAllowed(providerId, s.account.label, extensionId));
        if (!options.silent && !validSession) {
            await this.authenticationService.requestNewSession(providerId, scopes, options, extensionId, extensionName);
        }
        return validSession;
    }
    async $getSession(providerId, scopes, extensionId, extensionName, options) {
        const session = await this.doGetSession(providerId, scopes, extensionId, extensionName, options);
        if (session) {
            await this.authenticationService.addAccountUsage(providerId, session.account.label, extensionId, extensionName);
        }
        return session;
    }
    async selectSession(providerId, providerName, extensionId, extensionName, potentialSessions, scopes, options, clearSessionPreference) {
        if (!potentialSessions.length) {
            throw new Error('No potential sessions found');
        }
        if (clearSessionPreference) {
            await this.authenticationService.removeExtensionSessionId(extensionName, providerId);
        }
        else {
            const existingSessionPreference = await this.authenticationService.getExtensionSessionId(extensionName, providerId);
            if (existingSessionPreference) {
                const matchingSession = potentialSessions.find((session) => session.id === existingSessionPreference);
                if (matchingSession) {
                    const allowed = await this.$getSessionsPrompt(providerId, matchingSession.account.label, providerName, extensionId, extensionName);
                    if (allowed) {
                        return matchingSession;
                    }
                }
            }
        }
        const items = potentialSessions.map((session) => ({
            label: session.account.label,
            value: session,
        }));
        items.push({
            label: (0, ide_core_browser_1.localize)('authentication.useOtherAccount'),
            // 如果登录其他账户则放置一个 undefined 的值用来判断
            value: undefined,
        });
        const selectedSession = await this.quickPickService.show(items, {
            title: (0, ide_core_browser_1.formatLocalize)('authentication.selectAccount', extensionName, providerName),
            placeholder: (0, ide_core_browser_1.formatLocalize)('authentication.getSessionPlaceholder', extensionName),
            ignoreFocusOut: true,
        });
        const session = selectedSession !== null && selectedSession !== void 0 ? selectedSession : (await this.authenticationService.login(providerId, scopes, options));
        const accountName = session.account.label;
        const allowList = await this.authenticationService.getAllowedExtensions(providerId, accountName);
        if (!allowList.find((allowed) => allowed.id === extensionId)) {
            allowList.push({ id: extensionId, name: extensionName });
            await this.authenticationService.setAllowedExtensions(providerId, accountName, allowList);
        }
        await this.authenticationService.setExtensionSessionId(extensionName, providerId, session.id);
        return session;
    }
    async $getSessionsPrompt(providerId, accountName, providerName, extensionId, extensionName) {
        const allowList = await this.authenticationService.getAllowedExtensions(providerId, accountName);
        const extensionData = allowList.find((extension) => extension.id === extensionId);
        if (extensionData) {
            await this.authenticationService.addAccountUsage(providerId, accountName, extensionId, extensionName);
            return true;
        }
        const choice = await this.dialogService.info((0, ide_core_browser_1.formatLocalize)('authentication.confirmAuthenticationAccess', extensionName, providerName, accountName), [(0, ide_core_browser_1.localize)('ButtonCancel'), (0, ide_core_browser_1.localize)('ButtonAllow')]);
        const allow = choice === (0, ide_core_browser_1.localize)('ButtonAllow');
        if (allow) {
            this.authenticationService.addAccountUsage(providerId, accountName, extensionId, extensionName);
            allowList.push({ id: extensionId, name: extensionName });
            await this.authenticationService.setAllowedExtensions(providerId, accountName, allowList);
        }
        return allow;
    }
    // TODO: dialog detail 待实现
    async loginPrompt(providerName, extensionName, recreatingSession, _detail) {
        const choice = await this.dialogService.info(recreatingSession
            ? (0, ide_core_browser_1.formatLocalize)('authentication.confirmReLogin', extensionName, providerName)
            : (0, ide_core_browser_1.formatLocalize)('authentication.confirmLogin', extensionName, providerName), [(0, ide_core_browser_1.localize)('ButtonCancel'), (0, ide_core_browser_1.localize)('ButtonAllow')]);
        return choice === (0, ide_core_browser_1.localize)('ButtonAllow');
    }
    async $setTrustedExtensionAndAccountPreference(providerId, accountName, extensionId, extensionName, sessionId) {
        const allowList = await this.authenticationService.getAllowedExtensions(providerId, accountName);
        if (!allowList.find((allowed) => allowed.id === extensionId)) {
            allowList.push({ id: extensionId, name: extensionName });
            await this.authenticationService.setAllowedExtensions(providerId, accountName, allowList);
        }
        await this.authenticationService.setExtensionSessionId(extensionName, providerId, sessionId);
        await this.authenticationService.addAccountUsage(providerId, accountName, extensionId, extensionName);
    }
};
exports.MainThreadAuthentication = MainThreadAuthentication;
tslib_1.__decorate([
    (0, di_1.Autowired)(di_1.INJECTOR_TOKEN),
    tslib_1.__metadata("design:type", di_1.Injector)
], MainThreadAuthentication.prototype, "injector", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_common_1.IAuthenticationService),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthentication.prototype, "authenticationService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_overlay_1.IDialogService),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthentication.prototype, "dialogService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_overlay_1.IMessageService),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthentication.prototype, "messageService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(ide_core_browser_1.QuickPickService),
    tslib_1.__metadata("design:type", Object)
], MainThreadAuthentication.prototype, "quickPickService", void 0);
tslib_1.__decorate([
    (0, di_1.Autowired)(types_1.IActivationEventService),
    tslib_1.__metadata("design:type", types_1.IActivationEventService)
], MainThreadAuthentication.prototype, "activationService", void 0);
exports.MainThreadAuthentication = MainThreadAuthentication = tslib_1.__decorate([
    (0, di_1.Injectable)({ multiple: true }),
    tslib_1.__metadata("design:paramtypes", [Object])
], MainThreadAuthentication);
//# sourceMappingURL=main.thread.authentication.js.map