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

/**
 * @file Abstract module loader providing common functionality for all module loaders.
 */
sap.ui.define([
    "sap/base/Log",
    "sap/ushell/Config",
    "sap/ushell/utils",
    "sap/ushell/EventHub"
], (
    Log,
    Config,
    ushellUtils,
    EventHub
) => {
    "use strict";

    /**
     * Abstract module loader
     * @abstract
     *
     * @since 1.142.0
     * @private
     */
    class AbstractLoader {
        /**
         * Doables to be used for cleaning up configuration listeners.
         */
        #aDoables = [];

        /**
         * Loads the module of the given module path and creates an instance of it.
         *
         * @param {string} sModulePath The module path to load the module.
         * @param {object} oModuleAPI The module API instance.
         * @returns {Promise} A promise that resolves with the module instance.
         *
         * @since 1.142.0
         * @private
         */
        async loadModule (sModulePath, oModuleAPI) {
            const oModuleLoaded = Promise.withResolvers();
            EventHub.once("CoreResourcesComplementLoaded").do(async () => {
                const [Module] = await ushellUtils.requireAsync([sModulePath]);
                oModuleLoaded.resolve(new Module(oModuleAPI));
            });
            return oModuleLoaded.promise;
        }

        /**
         * Reads the ushell Config at the given path, parses it as JSON, and returns the resulting object.
         * If the configuration value is not set, it returns an empty object. It throws an error if parsing fails.
         *
         * @param {string} sPath The configuration path to read.
         * @returns {object} The parsed JSON object from the configuration or an empty object if not set.
         *
         * @since 1.142.0
         * @private
         */
        parseJSONFromConfig (sPath) {
            const sConfigValue = Config.last(sPath);
            if (sConfigValue) {
                try {
                    return JSON.parse(sConfigValue);
                } catch (oError) {
                    Log.error(`Failed to parse JSON from configuration at path: ${sPath}`, oError);
                }
            }
            return {};
        }

        /**
         * Registers a configuration listener for the given path and stores the doable for cleanup.
         * @param {string} sPath The configuration path to listen to.
         * @param {function} fnListener The listener function to be called on configuration changes.
         * @since 1.142.0
         * @private
         */
        registerConfigListener (sPath, fnListener) {
            this.#aDoables.push(Config.on(sPath).do(fnListener));
        }

        /**
         * Resets the loader by removing all configuration listeners.
         * @since 1.142.0
         * @private
         */
        async exit () {
            this.#aDoables.forEach((oDoable) => oDoable.off());
            this.#aDoables = [];
        }
    }
    return AbstractLoader;
});
