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

/**
 * @fileOverview Central utility for logging standardized deprecation messages.
 *
 * Provides a consistent format for deprecation errors across the SAP Fiori
 * Launchpad codebase. Supports deduplication to prevent console spam when
 * deprecated APIs are called repeatedly.
 *
 * @version 1.148.1
 */
sap.ui.define([
    "sap/base/Log"
], (
    Log
) => {
    "use strict";

    class DeprecationLog {
        #mLoggedKeys = new Set();

        /**
         * Builds a formatted deprecation message from the given subject and config.
         *
         * @param {string} sSubject The main subject part of the message
         * @param {object} oConfig The deprecation config
         * @param {string} [oConfig.successor] Successor API or module name
         * @param {string} [oConfig.since] Version since which the API is deprecated
         * @param {string} [oConfig.reason] Custom reason text
         * @returns {string} The formatted message
         */
        #buildMessage (sSubject, oConfig) {
            const aParts = [];

            if (oConfig.since) {
                aParts.push(`[since ${oConfig.since}]`);
            }

            aParts.push(sSubject);

            if (oConfig.successor) {
                aParts.push(`Please use '${oConfig.successor}' instead.`);
            } else if (oConfig.reason) {
                aParts.push(oConfig.reason);
            }

            return aParts.join(" ");
        }

        /**
         * Logs a deprecation error if the key has not been logged before.
         *
         * @param {string} sKey The deduplication key
         * @param {string} sMessage The error message
         * @param {string} sComponent The component name for Log.error
         */
        #log (sKey, sMessage, sComponent) {
            if (this.#mLoggedKeys.has(sKey)) {
                return;
            }
            this.#mLoggedKeys.add(sKey);
            Log.error(sMessage, null, sComponent);
        }

        /**
         * Derives a component name from a dotted path by removing the last segment.
         *
         * @param {string} sDottedPath A fully qualified dotted name (e.g. "sap.ushell.services.X.method")
         * @returns {string} The path without the last segment (e.g. "sap.ushell.services.X")
         */
        #deriveComponent (sDottedPath) {
            const iLastDot = sDottedPath.lastIndexOf(".");
            if (iLastDot > 0) {
                return sDottedPath.substring(0, iLastDot);
            }
            return sDottedPath;
        }

        /**
         * Logs a deprecation error for a specific API (method, parameter, or option).
         *
         * The message is logged once per unique <code>oConfig.api</code> value.
         * Subsequent calls with the same key are silently suppressed.
         *
         * @example
         * DeprecationLog.logApiDeprecation({
         *     api: "sap.ushell.services.URLParsing.isIntentUrl",
         *     successor: "sap.ushell.services.URLParsing.isIntentUrlAsync",
         *     since: "1.96"
         * });
         *
         * @param {object} oConfig Configuration for the deprecation log message
         * @param {string} oConfig.api Fully qualified name of the deprecated API
         *   (e.g. "sap.ushell.services.CrossApplicationNavigation.hrefForExternal")
         * @param {string} [oConfig.successor] Name of the successor API
         *   (e.g. "hrefForExternalAsync" or "CrossApplicationNavigation.getLinks")
         * @param {string} [oConfig.since] Version since which the API is deprecated (e.g. "1.94")
         * @param {string} [oConfig.reason] Custom reason or detail message.
         *   Used when no successor is available. Ignored if <code>successor</code> is set.
         * @param {string} [oConfig.component] The component name for the log entry.
         *   If omitted, derived from <code>oConfig.api</code> by removing the last dot-segment.
         * @since 1.148.0
         * @private
         */
        logApiDeprecation (oConfig) {
            let sSubject = `Deprecated API call of '${oConfig.api}'.`;
            if (!oConfig.successor && !oConfig.reason) {
                sSubject += " This API has been discontinued.";
            }
            const sMessage = this.#buildMessage(sSubject, oConfig);
            const sComponent = oConfig.component || this.#deriveComponent(oConfig.api);
            this.#log(oConfig.api, sMessage, sComponent);
        }

        /**
         * Logs a deprecation error for an entire service or module.
         *
         * The message is logged once per unique <code>oConfig.module</code> value.
         * Subsequent calls with the same key are silently suppressed.
         *
         * @example
         * DeprecationLog.logModuleDeprecation({
         *     module: "sap.ushell.services.CrossApplicationNavigation",
         *     successor: "sap.ushell.services.Navigation",
         *     since: "1.120"
         * });
         *
         * @param {object} oConfig Configuration for the deprecation log message
         * @param {string} oConfig.module Fully qualified name of the deprecated module
         *   (e.g. "sap.ushell.services.UsageAnalytics")
         * @param {string} [oConfig.successor] Fully qualified name of the replacement module or service
         * @param {string} [oConfig.since] Version since which the module is deprecated (e.g. "1.120")
         * @param {string} [oConfig.reason] Custom reason or detail message.
         *   Used when no successor is available. Ignored if <code>successor</code> is set.
         * @param {string} [oConfig.component] The component name for the log entry.
         *   Defaults to <code>oConfig.module</code> if omitted.
         * @since 1.148.0
         * @private
         */
        logModuleDeprecation (oConfig) {
            let sSubject = `'${oConfig.module}' is deprecated.`;
            if (!oConfig.successor && !oConfig.reason) {
                sSubject += " Remove all dependencies to this module.";
            }
            const sMessage = this.#buildMessage(sSubject, oConfig);
            const sComponent = oConfig.component || oConfig.module;
            this.#log(oConfig.module, sMessage, sComponent);
        }
    }

    return new DeprecationLog();
});
