// Copyright (c) 2009-2026 SAP SE, All Rights Reserved
(function () { // keep global namespace
    "use strict";

    /*
     * Copy from sap/ushell/bootstrap/Config!
    */
    function _migrateV2ServiceConfig (ushellConfig) {
        const ObjectPath = sap.ui.require("sap/base/util/ObjectPath");
        // migrate service config
        [
            { from: "services.Bookmark", to: "services.BookmarkV2" },
            { from: "services.CrossApplicationNavigation", to: "services.Navigation" },
            { from: "services.Notifications", to: "services.NotificationsV2" },
            { from: "services.Personalization", to: "services.PersonalizationV2" },
            { from: "services.LaunchPage", to: "services.FlpLaunchPage" },
            { from: "services.ShellNavigation", to: "services.ShellNavigationInternal" },
            { from: "services.NavTargetResolution", to: "services.NavTargetResolutionInternal" }
        ].forEach((oMigration) => {
            const sServiceFromName = oMigration.from.split(".")[1];
            const oConfigFrom = ObjectPath.get(oMigration.from, ushellConfig);
            const oConfigTo = ObjectPath.get(oMigration.to, ushellConfig);
            if (oConfigFrom && !oConfigTo) {
                // we only need a shallow copy here, because we only want to change the module property
                const oConfigFromCopy = { ...oConfigFrom };

                // the module references the standard service name this might lead to infinite loops
                // because the old service can depend on the new service
                // DINC0103566
                if (oConfigFromCopy.module === `sap.ushell.services.${sServiceFromName}`) {
                    delete oConfigFromCopy.module;
                }
                ObjectPath.set(oMigration.to, oConfigFromCopy, ushellConfig);
            }
        });
    }

    /**
     * Reads the ushell config from the meta tag 'sap.ushellConfig.ui5appruntime'. Isolated for better testability.
     * @returns {object} The parsed ushell config object or an empty object if parsing fails.
     * @private
     */
    function readUshellMetaTag () {
        const sMetaDataContent = document.querySelector("meta[name='sap.ushellConfig.ui5appruntime']")?.content;
        if (sMetaDataContent) {
            try {
                return JSON.parse(sMetaDataContent);
            } catch (e) {
                // ignore this for now. Better would be to log it but sap.base.Log is not available yet.
                // console.error(`Failed to parse ushell config from meta tag 'sap.ushellConfig.ui5appruntime': ${e}`);
            }
        }
        return {};
    }

    /**
     * Removes the <code>"sap-plugin-agent"</code> flag from the bootstrap WAPluginAgent plugin
     * config so that it is loaded unconditionally in the scube scenario.
     *
     * When <code>"sap-plugin-agent": true</code> is set on a plugin config, the blue-box
     * PluginManager only loads it when the matching parent-plugin name appears in the
     * <code>sap-plugins</code> URL parameter (see {@link sap.ushell.services.PluginManager}).
     * Removing the flag bypasses that gate, ensuring WAPluginAgent always loads in scube
     * regardless of the <code>sap-plugins</code> URL parameter.
     *
     * @param {object} oUshellConfig reference to the ushell config object
     * @private
     */
    function alwaysLoadWebAssistantPlugin (oUshellConfig) {
        // In scube, WAPluginAgent must load unconditionally. Removing "sap-plugin-agent" from the
        // bootstrap plugin config bypasses the PluginManager's parent-plugin gating check
        // (which is based on the "sap-plugins" URL parameter) so the agent always loads.
        const oPlugins = oUshellConfig.bootstrapPlugins;
        if (oPlugins && oPlugins.hasOwnProperty("WAPluginAgent") && oPlugins.WAPluginAgent.hasOwnProperty("config")) {
            delete oPlugins.WAPluginAgent.config["sap-plugin-agent"];
        }
    }

    /**
     * Composes the ushell config contents out of a specific ushell config (for meta tag) and default ushell configuration.
     * It additon the result might be affected by certain parameters in URL's search string.
     *
     * @param {object} oServerSideUshellConfig Configuration provided by the server (via meta tag)
     * @param {object} oUshellConfigDefaults Defaults for service adapter configurations, for example
     * @param {string} sWindowLocationSearch content from window.location.search is exepcted
     * @returns {object} the composed ushell config
     * @private
     */
    function createUshellConfig (oServerSideUshellConfig, oUshellConfigDefaults, sWindowLocationSearch) {
        const ObjectPath = sap.ui.require("sap/base/util/ObjectPath");
        const deepExtend = sap.ui.require("sap/base/util/deepExtend");

        let oShellConfig = oServerSideUshellConfig || {};
        // In preparation for UI5 2.0 some services got successors.
        // To allow these successor services to run easily, the migration of the old service is copied
        // to the new service, too.
        _migrateV2ServiceConfig(oShellConfig);
        oShellConfig = deepExtend({}, oUshellConfigDefaults, oShellConfig);

        const oUriParameters = new URLSearchParams(sWindowLocationSearch);

        if (oUriParameters.has("sap-spaces") && oUriParameters.get("sap-spaces") === "true") {
            ObjectPath.set("ushell.spaces.enabled", true, oShellConfig);
        }

        // for security reasons it is only possible to disable the personalization via URL parameter but not to enable it
        if (oUriParameters.has("sap-personalization") && oUriParameters.get("sap-personalization") === "false") {
            ObjectPath.set("renderers.fiori2.componentData.config.enablePersonalization", false, oShellConfig);
        }

        if (oUriParameters.has("sap-remote-intent")) { // S/CUBE detection
            alwaysLoadWebAssistantPlugin(oShellConfig);
        }

        return oShellConfig;
    }

    /**
     * Shows the busy indicator until an app has been loaded
     */
    function showBusyIndicator () {
        sap.ui.require(["sap/ui/core/BusyIndicator"], (BusyIndicator) => {
            try {
                if (apprtBIdiv) {
                    document.body.classList.remove("apprtBIbg");
                    apprtBIdiv.parentNode.removeChild(apprtBIdiv);
                    apprtBIstyle.parentNode.removeChild(apprtBIstyle);
                }
                BusyIndicator.show(0);
            } catch {
                BusyIndicator.show(0);
            }
        });
    }

    /**
     * Registers lightweight module stubs when running in UI5 AppRuntime so heavy ushell modules are not loaded.
     * This keeps the bootstrap lean by short-circuiting rarely used services and adapters in the appruntime path.
     *
     * @private
     */
    function registerAppRuntimeStubs () {
        if (document.URL.indexOf("ui5appruntime") > 0) {
            sap.ui.define("sap/ushell/ApplicationType/wdaResolution", [], () => { return {}; });
            sap.ui.define("sap/ushell/ApplicationType/guiResolution", [], () => { return {}; });
            if (document.URL.indexOf("sap-ui-app-id=") > 0) {
                sap.ui.define("sap/ushell/ApplicationType", [], () => {
                    return {
                        URL: {
                            type: "URL"
                        },
                        WDA: {
                            type: "WDA"
                        },
                        TR: {
                            type: "TR"
                        },
                        NWBC: {
                            type: "NWBC"
                        },
                        WCF: {
                            type: "WCF"
                        },
                        SAPUI5: {
                            type: "SAPUI5"
                        }
                    };
                });
            }
            sap.ui.define("sap/ushell/components/applicationIntegration/AppLifeCycle", [], () => { return {}; });
            sap.ui.define("sap/ushell/appIntegration/AppLifeCycle", [], () => { return {}; });
            sap.ui.define("sap/ushell/services/appstate/WindowAdapter", [], () => { return function () { }; });
            sap.ui.define("sap/ushell/services/appstate/SequentializingAdapter", [], () => { return function () { }; });
            sap.ui.define("sap/ushell/services/appstate/Sequentializer", [], () => { return function () { }; });
            sap.ui.define("sap/ushell/services/Configuration", [], () => {
                function Configuration () {
                    this.attachSizeBehaviorUpdate = function () { };
                    this.hasNoAdapter = true;
                }
                Configuration.hasNoAdapter = true;
                return Configuration;
            });
            sap.ui.define("sap/ushell/services/PluginManager/Extensions", [], () => { return function () { }; });
            sap.ui.define("sap/ushell/services/MessageBroker/MessageBrokerEngine", [], () => { return function () { }; });
            sap.ui.define("sap/ushell/bootstrap/common/common.load.core-min", [], () => {
                return {
                    loaded: false,
                    load: function (sPath) { }
                };
            });
        }
    }

    /**
     * @returns {object} the default shell configuration for the AppRuntime.
     * @private
     */
    function getDefaultShellConfig () {
        return {
            services: {
                CrossApplicationNavigation: {
                    module: "sap.ushell.appRuntime.ui5.services.CrossApplicationNavigation",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                Navigation: {
                    module: "sap.ushell.appRuntime.ui5.services.Navigation",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                NavTargetResolution: {
                    module: "sap.ushell.appRuntime.ui5.services.NavTargetResolution",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                NavTargetResolutionInternal: {
                    module: "sap.ushell.appRuntime.ui5.services.NavTargetResolutionInternal",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                ShellNavigation: {
                    module: "sap.ushell.appRuntime.ui5.services.ShellNavigation",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                ShellNavigationInternal: {
                    module: "sap.ushell.appRuntime.ui5.services.ShellNavigationInternal",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                ClientSideTargetResolution: {
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                AppConfiguration: {
                    module: "sap.ushell.appRuntime.ui5.services.AppConfiguration"
                },
                Bookmark: {
                    module: "sap.ushell.appRuntime.ui5.services.Bookmark",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                // Until AppRuntime switches to consumption of BookmarkV2, proxy its API to the Bookmark service
                BookmarkV2: {
                    module: "sap.ushell.appRuntime.ui5.services.BookmarkV2",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                Extension: {
                    module: "sap.ushell.appRuntime.ui5.services.Extension"
                },
                FrameBoundExtension: {
                    module: "sap.ushell.appRuntime.ui5.services.FrameBoundExtension"
                },
                LaunchPage: {
                    module: "sap.ushell.appRuntime.ui5.services.LaunchPage",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                FlpLaunchPage: {
                    module: "sap.ushell.appRuntime.ui5.services.FlpLaunchPage"
                },
                UserInfo: {
                    module: "sap.ushell.appRuntime.ui5.services.UserInfo",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                UserDefaultParameters: {
                    module: "sap.ushell.appRuntime.ui5.services.UserDefaultParameters",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                AppState: {
                    module: "sap.ushell.appRuntime.ui5.services.AppState",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                AppLifeCycle: {
                    module: "sap.ushell.appRuntime.ui5.services.AppLifeCycle",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                PluginManager: {
                    config: {
                        isBlueBox: true
                    }
                },
                CommonDataModel: {
                    module: "sap.ushell.appRuntime.ui5.services.CommonDataModel",
                    adapter: {
                        module: "sap.ushell.appRuntime.ui5.services.adapters.EmptyAdapter"
                    }
                },
                ReferenceResolver: {
                    module: "sap.ushell.appRuntime.ui5.services.ReferenceResolver"
                },
                MessageBroker: {
                    module: "sap.ushell.appRuntime.ui5.services.MessageBroker"
                },
                Ui5ComponentLoader: {
                    config: {
                        loadDefaultDependencies: false
                    }
                },
                Container: {
                    adapter: {
                        config: {
                            userProfile: {
                                defaults: {
                                    setThemePermitted: true
                                }
                            }
                        }
                    }
                }
            },
            ushell: {
                customPreload: {
                    enabled: false
                }
            }
        };
    }

    function prepareUshellConfig (
        fnReadUshellMetaTag = readUshellMetaTag,
        fnGetDefaultShellConfig = getDefaultShellConfig,
        sLocationSearch = window.location.search
    ) {
        window["sap-ushell-config"] = createUshellConfig(
            fnReadUshellMetaTag(),
            fnGetDefaultShellConfig(),
            sLocationSearch
        );
        return window["sap-ushell-config"];
    }

    function prepareLayout () {
        let oAppRuntimeContentRoot = document.querySelector("#content");
        if (!oAppRuntimeContentRoot) {
            // running in tests
            return;
        }

        if (oAppRuntimeContentRoot === document.body) {
            document.body.removeAttribute("id");
            oAppRuntimeContentRoot = document.createElement("div");
            oAppRuntimeContentRoot.setAttribute("id", "content");
            document.body.appendChild(oAppRuntimeContentRoot);
        } else {
            // This is an unexpected case.
            // All known Appruntime implementations use the content id on the body element.
        }

        document.body.setAttribute("style", "height: 100%; width: 100%;");
        oAppRuntimeContentRoot.setAttribute("style", "height: 100%; width: 100%;");
    }

    prepareLayout();
    /**
     * Must run before sap.ui.define(): some static dependencies below (e.g. Renderer) transitively
     * load "sap/ushell/Config", which reads window["sap-ushell-config"] exactly once at module
     * execution time — before the sap.ui.define() factory callback (main()) is called.
     *
     * Moving this into main() is not feasible (Option A): all deps that transitively pull in
     * sap/ushell/Config would need to become lazy requires issued after this call.
     * The verified chain is:
     *   Renderer → AppRuntime/Container → sap/ushell/Container
     *     → common.util → sap/ushell/Config  ← reads window["sap-ushell-config"] once
     *     → ShellLayout → ShellModel → sap/ushell/Config
     * sap/ushell/Container is a dependency of so many ushell modules that delaying its load in a
     * stable way is not feasible.
     */
    prepareUshellConfig();
    showBusyIndicator();
    registerAppRuntimeStubs();

    sap.ui.define([
        "sap/ushell/appRuntime/ui5/AppRuntime/AppRuntimeInternal"
    ], (appRuntime) => {
        /* global apprtBIdiv, apprtBIstyle */

        appRuntime.main();

        // Expose for Testing
        appRuntime._prepareUshellConfig = prepareUshellConfig;
        appRuntime._prepareLayout = prepareLayout;
        appRuntime._readUshellMetaTag = readUshellMetaTag;
        appRuntime._createUshellConfig = createUshellConfig;
        appRuntime._getDefaultShellConfig = getDefaultShellConfig;

        return appRuntime;
    });
}());
