// Copyright (c) 2009-2026 SAP SE, All Rights Reserved

sap.ui.define([
    "sap/ui/core/CustomData",
    "sap/ushell/Config",
    "sap/ushell/modules/loader/AbstractLoader",
    "sap/ushell/modules/Assistant/AssistantAPI",
    "sap/ushell/utils"
], (
    CustomData,
    Config,
    AbstractLoader,
    AssistantAPI,
    ushellUtils
) => {
    "use strict";
    const sConfigBasePath = "/core/assistant";

    /**
     * Assistant component for the Fiori Launchpad.
     *
     * @augments sap.ushell.modules.loader.AbstractLoader
     * @since 1.143.0
     * @private
     */

    class AssistantLoader extends AbstractLoader {
        /**
         * The instance of the loaded assistant module.
         *
         * @type {sap.ushell.modules.Assistant.AssistantProvider|null}
         * @since 1.143.0
         * @private
         */
        #oModuleInstance;

        /**
         * The API object passed to the assistant module.
         *
         * @type {sap.ushell.modules.Assistant.AssistantAPI}
         * @since 1.143.0
         * @private
         */
        #oModuleAPI;

        /**
         * A promise that resolves when the loader is initialized.
         * @since 1.143.0
         * @type {Promise}
         */
        #pInitialized;

        /**
         * Creates a new instance of the AssistantLoader.
         *
         * @since 1.143.0
         * @private
         */
        constructor () {
            super();
            this.#init();
        }

        /**
         * Initializes the loader by setting up a listener for configuration changes.
         * Loads or unloads the assistant module based on the configuration.
         *
         * @returns {Promise} A promise that resolves when the initialization is complete.
         * @since 1.143.0
         * @private
         */
        async #init () {
            this.registerConfigListener(sConfigBasePath, async () => {
                const bEnabled = Config.last(`${sConfigBasePath}/enabled`);
                await this.#cleanup();
                if (bEnabled) {
                    this.#oModuleInstance = await this.#load();
                }
                this.#pInitialized.resolve();
                this.#oModuleAPI.getAssistantButton()?.setEnabled(true);
            });
        }

        /**
         * Returns a promise that resolves when the loader is initialized.
         * @returns {Promise} A promise that resolves when the loader is initialized.
         * @since 1.143.0
         * @private
         */
        initialized () {
            return this.#pInitialized.promise;
        }

        /**
         * Loads the assistant module and creates an instance of it.
         *
         * @returns {Promise<sap.ushell.modules.Assistant.AssistantProvider>} A promise that resolves to the assistant module instance.
         * @since 1.143.0
         * @private
         */
        async #load () {
            const oVisualConfig = Config.last(`${sConfigBasePath}/visualConfiguration`);
            const [Button] = await ushellUtils.requireAsync(["sap/f/gen/ui5/webcomponents/dist/Button"]);
            const oAssistantButton = new Button(oVisualConfig.id, {
                icon: oVisualConfig.icon,
                enabled: false, // will be enabled after the module is loaded
                customData: new CustomData({key: "help-id", value: oVisualConfig.helpId, writeToDom: true})
            });

            this.#oModuleAPI = new AssistantAPI(oAssistantButton, this.parseJSONFromConfig(`${sConfigBasePath}/configuration`));

            // load the module
            return this.loadModule(Config.last(`${sConfigBasePath}/modulePath`), this.#oModuleAPI);
        }

        /**
         * Resets the loader by cleaning up the current module instance and re-initializing it.
         *
         * @returns {Promise} A promise that resolves when the module instance is destroyed and references are cleared.
         * @since 1.143.0
         * @private
         */
        async reset () {
            await this.exit();
            await this.#init();
        }

        /**
         * Cleans up the current module instance and clears the API reference.
         * @returns {Promise} A promise that resolves when the cleanup is complete.
         * @since 1.143.0
         * @private
         */
        async #cleanup () {
            if (this.#oModuleInstance?.exit) {
                await this.#oModuleInstance.exit();
            }

            this.#oModuleAPI?.getAssistantButton()?.destroy();
            this.#oModuleAPI = null;
            this.#oModuleInstance = null;
            this.#pInitialized = Promise.withResolvers();
        }

        /**
         * Cleans up the loader by resetting the module instance and calling the superclass exit method.
         *
         * @returns {Promise} A promise that resolves when the loader is cleaned up.
         * @since 1.143.0
         * @private
         * @override
         */
        async exit () {
            await super.exit();
            await this.#cleanup();
        }
    }
    return new AssistantLoader();
});
