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

sap.ui.define([
    "sap/base/Log"
], (
    Log
) => {
    "use strict";

    /**
     * @alias sap.ushell.services.PersonalizationV2.PendingOperations
     * @namespace
     * @description Manages pending operations for personalization service.
     * All operations for the same container are queued and executed in order.
     * Each operation can be invoked immediately or canceled and be followed by
     * a new operation.
     *
     * Once the 'run' callback is done it is removed from the queue.
     * When cancel or runImmediately is called before end it only awaits the cancel or
     * runImmediately result before starting the next operation.
     *
     * @since 1.144.0
     * @private
     */
    class PendingOperations {
        /**
         * A promise chain to ensure operations for the same container are executed in order.
         */
        #mOperationPromiseChain = new Map();

        /**
         * A map of pending operations, keyed by container ID.
         * @type {Map<string, sap.ushell.services.PersonalizationV2.Operation>}
         */
        #mCurrentOperation = new Map();

        /**
         * Runs the current operation for the given container immediately.
         * @param {string} sContainerKey The personalization container key.
         * @returns {Promise} Resolves when the operation is done running immediately.
         *
         * @since 1.144.0
         * @private
         */
        async #runCurrentOperationImmediately (sContainerKey) {
            const oOperation = this.#mCurrentOperation.get(sContainerKey);
            if (!oOperation) {
                return;
            }

            this.#mCurrentOperation.delete(sContainerKey);

            try {
                await oOperation.runImmediately();
            } catch (oError) {
                Log.error(`Failed to run current operation immediately for container ${sContainerKey}`, oError, "sap.ushell.services.PersonalizationV2");
            }
        }

        /**
         * Cancels the current operation for the given container.
         * @param {string} sContainerKey The personalization container key.
         * @returns {Promise} Resolves when the operation is canceled.
         *
         * @since 1.144.0
         * @private
         */
        async #cancelCurrentOperation (sContainerKey) {
            const oOperation = this.#mCurrentOperation.get(sContainerKey);
            if (!oOperation) {
                return;
            }

            Log.info(`Cancelling pending operation for container ${sContainerKey}`, null, "sap.ushell.services.PersonalizationV2");

            this.#mCurrentOperation.delete(sContainerKey);

            try {
                await oOperation.cancel();
            } catch (oError) {
                Log.error(`Failed to cancel pending operation for container ${sContainerKey}`, oError, "sap.ushell.services.PersonalizationV2");
            }
        }

        /**
         * Stores and runs the given operation for the specified container.
         * @param {string} sContainerKey The personalization container key.
         * @param {sap.ushell.services.PersonalizationV2.Operation} oOperation The operation to store and run.
         *
         * @since 1.144.0
         * @private
         */
        #storeAndRunOperation (sContainerKey, oOperation) {
            // store the new operation for next operation
            this.#mCurrentOperation.set(sContainerKey, oOperation);

            try {
                oOperation.run();
                // do not return here the result as this would block the promise chain
            } catch (oError) {
                Log.error(`Failed to run operation for container ${sContainerKey}`, oError, "sap.ushell.services.PersonalizationV2");
            }
        }

        /**
         * Runs the given callback as the next promise in the promise chain.
         * Awaits the previous state of the promise chain before executing the callback.
         * @param {string} sContainerKey The personalization container key.
         * @param {function (): Promise} fnAsyncCallback The async callback to run.
         * @returns {Promise} Resolves with the new state of the promise chain
         *
         * @since 1.144.0
         * @private
         */
        #runAsNextPromiseInTheChain (sContainerKey, fnAsyncCallback) {
            if (!this.#mOperationPromiseChain.has(sContainerKey)) {
                this.#mOperationPromiseChain.set(sContainerKey, Promise.resolve());
            }

            const pNewPromise = this.#mOperationPromiseChain.get(sContainerKey).then(fnAsyncCallback);

            this.#mOperationPromiseChain.set(sContainerKey, pNewPromise);

            return pNewPromise;
        }

        /**
         * Cancels the current operation and sets the new operation for the given container.
         * @param {string} sContainerKey The personalization container key.
         * @param {sap.ushell.services.PersonalizationV2.Operation} oOperation The function call to run
         * @returns {Promise} Resolves with the new state of the promise chain.
         * And additionally awaits the actual result.
         *
         * @since 1.144.0
         * @private
         */
        async cancelCurrentOperationAndSetNext (sContainerKey, oOperation) {
            return this.#runAsNextPromiseInTheChain(sContainerKey, async () => {
                await this.#cancelCurrentOperation(sContainerKey);

                this.#storeAndRunOperation(sContainerKey, oOperation);
                // do not return here the result as this would block the promise chain
            }).then(() => {
                return oOperation.awaitResult();
            });
        }

        /**
         * Cancels the current operation
         * @param {string} sContainerKey The personalization container key.
         * @returns {Promise} Resolves with the new state of the promise chain.
         * Resolves with the new state of the promise chain.
         *
         * @since 1.144.0
         * @private
         */
        async cancelCurrentOperation (sContainerKey) {
            return this.#runAsNextPromiseInTheChain(sContainerKey, async () => {
                await this.#cancelCurrentOperation(sContainerKey);
            });
        }

        /**
         * Runs the current operation immediately and sets the new operation for the given container.
         * @param {string} sContainerKey The personalization container key.
         * @param {sap.ushell.services.PersonalizationV2.Operation} oOperation The function call to run
         * @returns {Promise}
         * And additionally awaits the actual result.
         *
         * @since 1.144.0
         * @private
         */
        async runCurrentOperationImmediatelyAndSetNext (sContainerKey, oOperation) {
            return this.#runAsNextPromiseInTheChain(sContainerKey, async () => {
                await this.#runCurrentOperationImmediately(sContainerKey);

                this.#storeAndRunOperation(sContainerKey, oOperation);
                // do not return here the result as this would block the promise chain
            }).then(() => {
                return oOperation.awaitResult();
            });
        }

        /**
         * Runs the current operation immediately.
         * @param {string} sContainerKey The personalization container key.
         * @returns {Promise} Resolves with the new state of the promise chain.
         *
         * @since 1.144.0
         * @private
         */
        async runCurrentOperationImmediately (sContainerKey) {
            return this.#runAsNextPromiseInTheChain(sContainerKey, async () => {
                await this.#runCurrentOperationImmediately(sContainerKey);
            });
        }

        /**
         * Awaits all pending operations for all containers.
         *
         * @since 1.144.0
         * @private
         */
        async awaitAllOperations () {
            await Promise.allSettled(this.#mOperationPromiseChain.values());
        }
    }

    return new PendingOperations();
});
