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

sap.ui.define([
    "sap/ushell/Config",
    "sap/ushell/Container",
    "sap/ushell/modules/loader/AbstractLoader",
    "sap/ushell/modules/Search/SearchAPI",
    "sap/ushell/utils/UrlParsing"
], (
    Config,
    Container,
    AbstractLoader,
    SearchAPI,
    UrlParsing
) => {
    "use strict";
    const sConfigBasePath = "/core/search";

    /**
     * Search module loader for the Fiori Launchpad.
     *
     * @since 1.142.0
     * @private
     */
    class SearchLoader extends AbstractLoader {
        /**
         * The module instance.
         * @type {sap.ushell.modules.Search.SearchProvider}
         */
        #oModuleInstance;

        /**
         * The module API instance.
         * @type {sap.ushell.modules.Search.SearchAPI}
         */
        #oModuleAPI;

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

        constructor () {
            super();
            this.#init();
        }

        #init () {
            this.registerConfigListener(sConfigBasePath, async () => {
                const bEnabled = Config.last(`${sConfigBasePath}/enabled`);
                await this.#cleanup();

                if (bEnabled) {
                    this.#oModuleInstance = await this.#load();
                }
                this.#pInitialized.resolve();
            });
        }

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

        /**
         * Loads the search module and creates an instance of it.
         *
         * @returns {Promise<sap.ushell.modules.Search.SearchProvider>} A promise that resolves to the search module instance.
         * @since 1.142.0
         * @private
         */
        async #load () {
            this.#oModuleAPI = new SearchAPI(this.parseJSONFromConfig(`${sConfigBasePath}/configuration`));

            const AppLifeCycle = await Container.getServiceAsync("AppLifeCycle");
            AppLifeCycle.attachAppLoaded(this.#handleSearchFieldExpandState, this);

            this.#handleSearchFieldExpandState();

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

        /**
         * Handles the search field expand state based on the appOpened event. If the search result app is opened,
         * it sets the search field to be shown.
         *
         * @since 1.142.0
         * @private
         */
        async #handleSearchFieldExpandState () {
            const AppLifeCycle = await Container.getServiceAsync("AppLifeCycle");
            const oCurrentApplication = await AppLifeCycle.getCurrentApplication();
            const oAppInfo = await oCurrentApplication.getInfo(["appIntent"]);

            const sSearchResultAppIntent = Config.last(`${sConfigBasePath}/searchResultAppIntent`);
            if (UrlParsing.compareHashes(oAppInfo.appIntent, sSearchResultAppIntent).sameIntent) {
                this.#oModuleAPI.setShowSearchField(true);
            }
        }

        /**
         * 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.142.0
         * @private
         */
        async reset () {
            await this.exit();
            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.142.0
         * @private
         */
        async #cleanup () {
            if (this.#oModuleInstance) {
                await this.#oModuleInstance.exit();
            }

            try {
                const AppLifeCycle = await Container.getServiceAsync("AppLifeCycle");
                AppLifeCycle.detachAppLoaded(this.#handleSearchFieldExpandState, this);
            } catch { /* Fail silently when Service not available */ }

            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.142.0
         * @private
         * @override
         */
        async exit () {
            await super.exit();
            await this.#cleanup();
        }
    }
    return new SearchLoader();
});
