/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Extensions
 */
import { Logger } from "@itwin/core-bentley";
import { FrontendLoggerCategory } from "../common/FrontendLoggerCategory";
/** The Extension Admin controls the list of currently loaded Extensions.
 *
 * @alpha
 */
export class ExtensionAdmin {
    /** Defines the set of extensions that are currently known and can be invoked during activation events.  */
    _extensions = new Map();
    _hosts;
    /** Fired when an Extension has been added or removed.
     * @internal
     */
    onStartup = async () => {
        await this.activateExtensionEvents("onStartup");
    };
    constructor() {
        this._hosts = [];
    }
    /**
     * Adds an extension.
     * The manifest will be fetched and the extension will be activated on an activation event.
     * @param provider
     * @alpha
     */
    async addExtension(provider) {
        if (provider.hostname) {
            const hostName = provider.hostname;
            if (this._hosts.length > 0 && this._hosts.indexOf(hostName) < 0) {
                throw new Error(`Error loading extension: ${hostName} was not registered.`);
            }
        }
        try {
            const manifest = await provider.getManifest();
            this._extensions.set(manifest.name, {
                manifest,
                provider,
            });
            // TODO - temporary fix to execute the missed startup event
            if (manifest.activationEvents.includes("onStartup"))
                provider.execute(); // eslint-disable-line @typescript-eslint/no-floating-promises
        }
        catch (e) {
            throw new Error(`Failed to get extension manifest ${provider.hostname ? `at ${provider.hostname}` : ""}: ${String(e)}`);
        }
    }
    /**
     * Adds a list of extensions
     * @param providers
     * @alpha
     */
    async addExtensions(providers) {
        return Promise.all(providers.map(async (provider) => this.addExtension(provider)));
    }
    /**
     * Registers a hostname for an extension.
     * Once a hostname has been registered, only remote extensions from registered hosts are permitted to be added.
     * @param hostUrl (string) Accepts both URLs and hostnames (e.g., http://localhost:3000, yourdomain.com, https://www.yourdomain.com, etc.).
     */
    registerHost(hostUrl) {
        const hostname = this.getHostName(hostUrl);
        if (this._hosts.indexOf(hostname) < 0) {
            this._hosts.push(hostname);
        }
    }
    /** Returns the hostname of an input string. Throws an error if input is not a valid hostname (or URL). */
    getHostName(inputUrl) {
        // inputs without a protocol (e.g., http://) will throw an error in URL constructor
        const inputWithProtocol = /(http|https):\/\//.test(inputUrl) ?
            inputUrl :
            `https://${inputUrl}`;
        try {
            const hostname = new URL(inputWithProtocol).hostname.replace("www.", "");
            return hostname;
        }
        catch (e) {
            if (e instanceof TypeError) {
                throw new Error("Argument hostUrl should be a valid URL or hostname (i.e. http://localhost:3000, yourdomain.com, etc.).");
            }
            throw e;
        }
    }
    /** Loops over all enabled Extensions and triggers each one if the provided event is defined. */
    async activateExtensionEvents(event) {
        for (const extension of this._extensions.values()) {
            if (!extension.manifest.activationEvents)
                continue;
            for (const activationEvent of extension.manifest.activationEvents) {
                if (activationEvent === event) {
                    this._execute(extension); // eslint-disable-line @typescript-eslint/no-floating-promises
                }
            }
        }
    }
    /** Executes the extension. Catches and logs any errors (so that an extension will not crash the main application). */
    async _execute(extension) {
        try {
            await extension.provider.execute();
        }
        catch (e) {
            Logger.logError(FrontendLoggerCategory.Extensions, `Error executing extension ${extension.manifest.name}: ${String(e)}`);
        }
    }
}
//# sourceMappingURL=ExtensionAdmin.js.map