
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { localize, localize2 } from '../../../../nls.js';
import { toDisposable, combinedDisposable } from '../../../../base/common/lifecycle.js';
import { URI } from '../../../../base/common/uri.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.service.js';
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.service.js';
import { StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
import { IStorageService } from '../../../../platform/storage/common/storage.service.js';
import { IURLService } from '../../../../platform/url/common/url.service.js';
import { IHostService } from '../../host/browser/host.service.js';
import { ActivationKind } from '../common/extensions.js';
import { IExtensionService } from '../common/extensions.service.js';
import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
import '../../../../platform/instantiation/common/extensions.js';
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js';
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.service.js';
import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js';
import { IProductService } from '../../../../platform/product/common/productService.service.js';
import { disposableWindowInterval } from '../../../../base/browser/dom.js';
import { mainWindow } from '../../../../base/browser/window.js';
import { ICommandService } from '../../../../platform/commands/common/commands.service.js';
import { isCancellationError } from '../../../../base/common/errors.js';
import { INotificationService } from '../../../../platform/notification/common/notification.service.js';
import { MarkdownString } from '../../../../base/common/htmlContent.js';
import { equalsIgnoreCase } from '../../../../base/common/strings.js';
import '../../../../platform/instantiation/common/instantiation.js';

var ExtensionUrlBootstrapHandler_1;
const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_SECONDS = 30 * 1000;
const URL_TO_HANDLE = "extensionUrlHandler.urlToHandle";
const USER_TRUSTED_EXTENSIONS_CONFIGURATION_KEY = "extensions.confirmedUriHandlerExtensionIds";
const USER_TRUSTED_EXTENSIONS_STORAGE_KEY = "extensionUrlHandler.confirmedExtensions";
function isExtensionId(value) {
    return /^[a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*$/i.test(value);
}
class UserTrustedExtensionIdStorage {
    get extensions() {
        const userTrustedExtensionIdsJson = this.storageService.get(USER_TRUSTED_EXTENSIONS_STORAGE_KEY, StorageScope.PROFILE, "[]");
        try {
            return JSON.parse(userTrustedExtensionIdsJson);
        } catch {
            return [];
        }
    }
    constructor(storageService) {
        this.storageService = storageService;
    }
    has(id) {
        return this.extensions.indexOf(id) > -1;
    }
    add(id) {
        this.set([...this.extensions, id]);
    }
    set(ids) {
        this.storageService.store(
            USER_TRUSTED_EXTENSIONS_STORAGE_KEY,
            JSON.stringify(ids),
            StorageScope.PROFILE,
            StorageTarget.MACHINE
        );
    }
}
class ExtensionUrlHandlerOverrideRegistry {
    static {
        this.handlers = ( new Set());
    }
    static registerHandler(handler) {
        this.handlers.add(handler);
        return toDisposable(() => this.handlers.delete(handler));
    }
    static getHandler(uri) {
        for (const handler of this.handlers) {
            if (handler.canHandleURL(uri)) {
                return handler;
            }
        }
        return undefined;
    }
}
let ExtensionUrlHandler = class ExtensionUrlHandler {
    constructor(
        urlService,
        extensionService,
        dialogService,
        commandService,
        hostService,
        storageService,
        configurationService,
        notificationService,
        productService
    ) {
        this.extensionService = extensionService;
        this.dialogService = dialogService;
        this.commandService = commandService;
        this.hostService = hostService;
        this.storageService = storageService;
        this.configurationService = configurationService;
        this.notificationService = notificationService;
        this.productService = productService;
        this.extensionHandlers = ( new Map());
        this.uriBuffer = ( new Map());
        this.userTrustedExtensionsStorage = ( new UserTrustedExtensionIdStorage(storageService));
        const interval = disposableWindowInterval(mainWindow, () => this.garbageCollect(), THIRTY_SECONDS);
        const urlToHandleValue = this.storageService.get(URL_TO_HANDLE, StorageScope.WORKSPACE);
        if (urlToHandleValue) {
            this.storageService.remove(URL_TO_HANDLE, StorageScope.WORKSPACE);
            this.handleURL(URI.revive(JSON.parse(urlToHandleValue)), {
                trusted: true
            });
        }
        const cache = ExtensionUrlBootstrapHandler.cache;
        const drainTimeout = setTimeout(() => cache.forEach(([uri, option]) => this.handleURL(uri, option)));
        this.disposable = combinedDisposable(
            urlService.registerHandler(this),
            interval,
            toDisposable(() => clearTimeout(drainTimeout))
        );
    }
    async handleURL(uri, options) {
        if (!isExtensionId(uri.authority)) {
            return false;
        }
        const overrideHandler = ExtensionUrlHandlerOverrideRegistry.getHandler(uri);
        if (overrideHandler) {
            const handled = await overrideHandler.handleURL(uri);
            if (handled) {
                return handled;
            }
        }
        const extensionId = uri.authority;
        const initialHandler = this.extensionHandlers.get(ExtensionIdentifier.toKey(extensionId));
        let extensionDisplayName;
        if (!initialHandler) {
            const extension = await this.extensionService.getExtension(extensionId);
            if (!extension) {
                await this.handleUnhandledURL(uri, extensionId, options);
                return true;
            } else {
                extensionDisplayName = extension.displayName ?? "";
            }
        } else {
            extensionDisplayName = initialHandler.extensionDisplayName;
        }
        const trusted = options?.trusted || this.productService.trustedExtensionProtocolHandlers?.some(value => equalsIgnoreCase(value, extensionId)) || this.didUserTrustExtension(ExtensionIdentifier.toKey(extensionId));
        if (!trusted) {
            const uriString = ( uri.toString(false));
            let uriLabel = uriString;
            if (uriLabel.length > 40) {
                uriLabel = `${uriLabel.substring(0, 30)}...${uriLabel.substring(uriLabel.length - 5)}`;
            }
            const result = await this.dialogService.confirm({
                message: ( localize(16969, "Allow '{0}' extension to open this URI?", extensionDisplayName)),
                checkbox: {
                    label: ( localize(16970, "Do not ask me again for this extension"))
                },
                primaryButton: ( localize(16971, "&&Open")),
                custom: {
                    markdownDetails: [{
                        markdown: ( new MarkdownString(`<div title="${uriString}" aria-label='${uriString}'>${uriLabel}</div>`, {
                            supportHtml: true
                        }))
                    }]
                }
            });
            if (!result.confirmed) {
                return true;
            }
            if (result.checkboxChecked) {
                this.userTrustedExtensionsStorage.add(ExtensionIdentifier.toKey(extensionId));
            }
        }
        const handler = this.extensionHandlers.get(ExtensionIdentifier.toKey(extensionId));
        if (handler) {
            if (!initialHandler) {
                return await this.handleURLByExtension(extensionId, handler, uri, options);
            }
            return false;
        }
        const timestamp = ( new Date()).getTime();
        let uris = this.uriBuffer.get(ExtensionIdentifier.toKey(extensionId));
        if (!uris) {
            uris = [];
            this.uriBuffer.set(ExtensionIdentifier.toKey(extensionId), uris);
        }
        uris.push({
            timestamp,
            uri
        });
        await this.extensionService.activateByEvent(
            `onUri:${ExtensionIdentifier.toKey(extensionId)}`,
            ActivationKind.Immediate
        );
        return true;
    }
    registerExtensionHandler(extensionId, handler) {
        this.extensionHandlers.set(ExtensionIdentifier.toKey(extensionId), handler);
        const uris = this.uriBuffer.get(ExtensionIdentifier.toKey(extensionId)) || [];
        for (const {
            uri
        } of uris) {
            this.handleURLByExtension(extensionId, handler, uri);
        }
        this.uriBuffer.delete(ExtensionIdentifier.toKey(extensionId));
    }
    unregisterExtensionHandler(extensionId) {
        this.extensionHandlers.delete(ExtensionIdentifier.toKey(extensionId));
    }
    async handleURLByExtension(extensionId, handler, uri, options) {
        return await handler.handleURL(uri, options);
    }
    async handleUnhandledURL(uri, extensionId, options) {
        try {
            await this.commandService.executeCommand("workbench.extensions.installExtension", extensionId, {
                justification: {
                    reason: `${( localize(16972, "This extension wants to open a URI:"))}\n${( uri.toString())}`,
                    action: ( localize(16973, "Open URI"))
                },
                enable: true,
                installPreReleaseVersion: this.productService.quality !== "stable"
            });
        } catch (error) {
            if (!isCancellationError(error)) {
                this.notificationService.error(error);
            }
            return;
        }
        const extension = await this.extensionService.getExtension(extensionId);
        if (extension) {
            await this.handleURL(uri, {
                ...options,
                trusted: true
            });
        }
        else {
            const result = await this.dialogService.confirm({
                message: ( localize(
                    16974,
                    "Extension '{0}' is not loaded. Would you like to reload the window to load the extension and open the URL?",
                    extensionId
                )),
                primaryButton: ( localize(16975, "&&Reload Window and Open"))
            });
            if (!result.confirmed) {
                return;
            }
            this.storageService.store(
                URL_TO_HANDLE,
                JSON.stringify(uri.toJSON()),
                StorageScope.WORKSPACE,
                StorageTarget.MACHINE
            );
            await this.hostService.reload();
        }
    }
    garbageCollect() {
        const now = ( new Date()).getTime();
        const uriBuffer = ( new Map());
        this.uriBuffer.forEach((uris, extensionId) => {
            uris = uris.filter((
                {
                    timestamp
                }
            ) => now - timestamp < FIVE_MINUTES);
            if (uris.length > 0) {
                uriBuffer.set(extensionId, uris);
            }
        });
        this.uriBuffer = uriBuffer;
    }
    didUserTrustExtension(id) {
        if (( this.userTrustedExtensionsStorage.has(id))) {
            return true;
        }
        return this.getConfirmedTrustedExtensionIdsFromConfiguration().indexOf(id) > -1;
    }
    getConfirmedTrustedExtensionIdsFromConfiguration() {
        const trustedExtensionIds = this.configurationService.getValue(USER_TRUSTED_EXTENSIONS_CONFIGURATION_KEY);
        if (!Array.isArray(trustedExtensionIds)) {
            return [];
        }
        return trustedExtensionIds;
    }
    dispose() {
        this.disposable.dispose();
        this.extensionHandlers.clear();
        this.uriBuffer.clear();
    }
};
ExtensionUrlHandler = ( __decorate([( __param(0, IURLService)), ( __param(1, IExtensionService)), ( __param(2, IDialogService)), ( __param(3, ICommandService)), ( __param(4, IHostService)), ( __param(5, IStorageService)), ( __param(6, IConfigurationService)), ( __param(7, INotificationService)), ( __param(8, IProductService))], ExtensionUrlHandler));
let ExtensionUrlBootstrapHandler = class ExtensionUrlBootstrapHandler {
    static {
        ExtensionUrlBootstrapHandler_1 = this;
    }
    static {
        this.ID = "workbench.contrib.extensionUrlBootstrapHandler";
    }
    static {
        this._cache = [];
    }
    static get cache() {
        ExtensionUrlBootstrapHandler_1.disposable.dispose();
        const result = ExtensionUrlBootstrapHandler_1._cache;
        ExtensionUrlBootstrapHandler_1._cache = [];
        return result;
    }
    constructor(urlService) {
        ExtensionUrlBootstrapHandler_1.disposable = urlService.registerHandler(this);
    }
    async handleURL(uri, options) {
        if (!isExtensionId(uri.authority)) {
            return false;
        }
        ExtensionUrlBootstrapHandler_1._cache.push([uri, options]);
        return true;
    }
};
ExtensionUrlBootstrapHandler = ExtensionUrlBootstrapHandler_1 = ( __decorate([( __param(0, IURLService))], ExtensionUrlBootstrapHandler));
registerWorkbenchContribution2(
    ExtensionUrlBootstrapHandler.ID,
    ExtensionUrlBootstrapHandler,
    WorkbenchPhase.BlockRestore
);
class ManageAuthorizedExtensionURIsAction extends Action2 {
    constructor() {
        super({
            id: "workbench.extensions.action.manageAuthorizedExtensionURIs",
            title: ( localize2(16976, "Manage Authorized Extension URIs...")),
            category: ( localize2(16977, "Extensions")),
            menu: {
                id: MenuId.CommandPalette,
                when: ( IsWebContext.toNegated())
            }
        });
    }
    async run(accessor) {
        const storageService = accessor.get(IStorageService);
        const quickInputService = accessor.get(IQuickInputService);
        const storage = ( new UserTrustedExtensionIdStorage(storageService));
        const items = ( storage.extensions.map(label => ({
            label,
            picked: true
        })));
        if (items.length === 0) {
            await quickInputService.pick([{
                label: ( localize(16978, "There are currently no authorized extension URIs."))
            }]);
            return;
        }
        const result = await quickInputService.pick(items, {
            canPickMany: true
        });
        if (!result) {
            return;
        }
        storage.set(( result.map(item => item.label)));
    }
}
registerAction2(ManageAuthorizedExtensionURIsAction);

export { ExtensionUrlHandler, ExtensionUrlHandlerOverrideRegistry };
