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

/**
 * @file This module reads the "sap-ushell-xx-overwrite-config" query parameter from the URL
 * and generates an object out of it which can be merged with the ushell config.
 *
 * @private
 */
sap.ui.define([
    "sap/base/Log",
    "sap/base/util/ObjectPath"
], (
    Log,
    ObjectPath
) => {
    "use strict";

    class UrlConfigOverwrites {
        /*
         * Possible block list entries:
         *     - "a/b/c": [] --> a/b/c cannot be overwritten at all
         *     - "a/b/c": [ 1, 2 ] --> a/b/c cannot be overwritten with the values 1 or 2 but with any other value like 3
         */
        #oBlockList = {
            "renderers/fiori2/componentData/config/enablePersonalization": [true], // switch off only

            // Session handling
            "renderers/fiori2/componentData/config/sessionTimeoutReminderInMinutes": [],
            "renderers/fiori2/componentData/config/sessionTimeoutIntervalInMinutes": [],
            "renderers/fiori2/componentData/config/sessionTimeoutTileStopRefreshIntervalInMinutes": [],
            "renderers/fiori2/componentData/config/enableAutomaticSignout": [false], // switch on only

            // abap user must no be overwritten
            "services/Container/adapter/config/id": [], // user id
            "services/Container/adapter/config/firstName": [],
            "services/Container/adapter/config/lastName": [],
            "services/Container/adapter/config/fullName": [],

            // cdm user must no be overwritten
            "services/Container/adapter/config/userProfile/defaults/id": [], // user id
            "services/Container/adapter/config/userProfile/defaults/firstName": [],
            "services/Container/adapter/config/userProfile/defaults/lastName": [],
            "services/Container/adapter/config/userProfile/defaults/fullName": [],

            // The usageRecorder serviceUrl must not be overwritten for security reasons
            "services/NavTargetResolution/config/usageRecorder/serviceUrl": [],
            "services/NavTargetResolutionInternal/config/usageRecorder/serviceUrl": []
        };

        #isBlockListed (oConsideredBlockList, oEntry) {
            let sFullPropertyPath = "";

            if (oEntry.namespace) {
                sFullPropertyPath = `${oEntry.namespace}/`;
            }
            sFullPropertyPath += oEntry.propertyName;

            const aBlockListEntry = oConsideredBlockList[sFullPropertyPath];
            if (!aBlockListEntry) {
                return false;
            }

            if (Array.isArray(aBlockListEntry)) {
                if (aBlockListEntry.length === 0) {
                    return true;
                }
                // check if the value is on the value block list
                for (let i = 0; i < aBlockListEntry.length; i++) {
                    if (aBlockListEntry[i] === oEntry.value) {
                        return true;
                    }
                }
                // the specified value is not on the block list
                return false;
            }

            throw new Error(`Block list entry "${sFullPropertyPath}" has an unknown type`);
        }

        #parseValue (sValue) {
            // booleans
            if (sValue === "false") {
                return false;
            }
            if (sValue === "true") {
                return true;
            }

            // numbers
            const nTempNumber = Number.parseFloat(sValue);
            if (!Number.isNaN(nTempNumber)) {
                return nTempNumber;
            }

            // just a string
            return sValue;
        }

        /*
         * Example usages of "sap-ushell-xx-overwrite-config":
         *   ?sap-ushell-xx-overwrite-config=renderers/fiori2/componentData/config/enablePersonalization:false
         *   ?sap-ushell-xx-overwrite-config=ushell/spaces/enabled:false,ushell/spaces/configurable:false
         *   ?sap-ushell-xx-overwrite-config=startupConfig/spacesMyhome:false,ushell/homeApp/component:false
         *   ?sap-ushell-xx-overwrite-config=
         *   ushell/companyLogo/url:data\:image/png;base64\,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
         */
        getConfig () {
            const oWindowLocation = this.getWindowLocation();
            const oFinalConfig = {};
            // to conform with the specification of the query string ("application/x-www-form-urlencoded")
            // see: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
            const sQueryString = oWindowLocation.search.replaceAll("+", " ");

            // find the parameter value
            const aParsedQueryParamValue = /[?&]sap-ushell-xx-overwrite-config=([^&]*)(&|$)/.exec(sQueryString);
            if (aParsedQueryParamValue === null) {
                return {};
            }

            // commas (",") can be used to overwrite multiple "parameter:value" pairs at the same time
            // the negative lookbehind is to allow commas to be escaped with a backslash ("\")
            const aOverwrittenEntryCandidates = decodeURIComponent(aParsedQueryParamValue[1])
                .split(/(?<!\\),/)
                .map((sOverwrittenEntryCandidate) => sOverwrittenEntryCandidate.replaceAll("\\,", ","));

            const aOverwrittenEntries = aOverwrittenEntryCandidates.reduce((aEntries, sCandidate) => {
                // a colon (":") is used to separate each parameter from its value within each "parameter:value" pair
                // the negative lookbehind is to allow colons to be escaped with a backslash ("\")
                const aParts = sCandidate
                    .split(/(?<!\\):/)
                    .map((sPart) => sPart.replaceAll("\\:", ":"));

                // namespaces may contain a leading "/" (e.g. for "sap-ui-debug")
                const aNamespaceParts = /^\/?(.*)\/([^/]*)$/.exec(aParts[0]);
                if (aNamespaceParts === null) {
                    Log.warning(`Ignoring invalid parameter for "sap-ushell-xx-overwrite-config": "${aParts[0]}"`);
                    return aEntries;
                }

                const oEntry = {
                    namespace: aNamespaceParts[1],
                    propertyName: aNamespaceParts[2],
                    value: this.#parseValue(aParts[1])
                };

                if (this.#isBlockListed(this.#oBlockList, oEntry)) {
                    Log.warning(`Ignoring restricted parameter for "sap-ushell-xx-overwrite-config": "${aParts[0]}"`);
                    return aEntries;
                }

                aEntries.push(oEntry);
                return aEntries;
            }, []);

            // convert entries to config
            aOverwrittenEntries.forEach((oOverwrite) => {
                const vValue = oOverwrite.value;
                const sNamespace = oOverwrite.namespace.replace(/\//g, ".");
                let oNamespace = ObjectPath.get(sNamespace, oFinalConfig);

                if (oNamespace === undefined) {
                    // create namespace as not existing yet
                    ObjectPath.set(sNamespace, {}, oFinalConfig);
                    oNamespace = ObjectPath.get(sNamespace, oFinalConfig);
                }

                oNamespace[oOverwrite.propertyName] = vValue;
            });

            return oFinalConfig;
        }

        getWindowLocation () {
            return window.location;
        }
    }

    return new UrlConfigOverwrites();
});
