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

/**
 * @fileOverview The Search API for the Fiori Launchpad.
 */

sap.ui.define([
    "sap/base/Log",
    "sap/ui/core/Element",
    "sap/ushell/modules/BaseAPI",
    "sap/ushell/state/ShellModel"
], (
    Log,
    Element,
    BaseAPI,
    ShellModel
) => {
    "use strict";
    /**
     * @alias sap.ushell.modules.Search.SearchAPI
     * @class
     * @description The API object for the Search module.
     * This module cannot be required it gets injected in the constructor of the Search module.
     * @see sap.ushell.modules.Search.Search
     *
     * @hideconstructor
     *
     * @since 1.142.0
     * @private
     * @ui5-restricted HAN-AS-INA-UI
     */
    class SearchAPI extends BaseAPI {
        /**
         * The instance of the ShellBar or ShellHeader depending on the ushell Config.
         *
         * @type {sap.f.gen.ui5.webcomponents_fiori.dist.ShellBar|sap.ushell.ui.ShellHeader}
         * @since 1.142.0
         * @private
         */
        #oShellBar;

        constructor (oConfig) {
            super(oConfig);
            this.#oShellBar = Element.getElementById("shell-header")?.getComponentInstance?.()?.getRootControl()?.byId("shellBar");
            if (!this.#oShellBar) {
                // use the ShellHeader for compatibility reasons
                this.#oShellBar = Element.getElementById("shell-header");
            }
        }

        /**
         * Gets the search button by ID as it would be expected in the ShellHeader.
         * In case the ShellBar is used this method returns undefined as the button is not available.
         *
         * @returns {sap.ushell.ui.shell.ShellHeadItem|undefined} The search button or undefined if not available.
         * @since 1.142.0
         * @private
         */
        #getShellHeaderSearchButton () {
            if (this.#isShellBar()) {
                // only available in ShellHeader
                return;
            }
            return Element.getElementById("sf");
        }

        /**
         * Checks if the ShellBar instance is available. This is used as long as the ShellHeader may still be used.
         *
         * @returns {boolean} True if the ShellBar instance is available, false otherwise.
         * @since 1.142.0
         * @private
         */
        #isShellBar () {
            return this.#oShellBar.isA("sap.f.gen.ui5.webcomponents_fiori.dist.ShellBar");
        }

        /**
         * Gets the search field instance.
         * The search field is not visible by default. The search module has to set it to visible by calling setVisible(true). This allows to get the search field prepared before showing it.
         * In case the ShellHeader is used this method returns undefined as the search field is not available.
         * @returns {Promise<sap.f.gen.ui5.webcomponents_fiori.dist.ShellBarSearch>|undefined} A promise that resolves to the search field instance or undefined if not available.
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        async getSearchField () {
            if (this.#isShellBar()) {
                const oSearchield = await this.#oShellBar.getSearchField()[0];
                if (!oSearchield.bSetVisibleOverridden) {
                    oSearchield.bSetVisibleOverridden = true;
                    const fnOriginalSetVisible = oSearchield.setVisible;
                    // override setVisible to take into account the search availability
                    // This seems to be the better option compared to providing a function
                    // on the search API. By this, the visibility is always in sync.
                    oSearchield.setVisible = function (...args) {
                        let [bVisible] = args;
                        const bSearchAvailable = ShellModel.getConfigModel().getProperty("/searchAvailable");
                        bVisible = bVisible && bSearchAvailable;
                        fnOriginalSetVisible.apply(oSearchield, [bVisible, ...args.slice(1)]);
                        if (!bSearchAvailable) {
                            Log.warn("Search is not available. The search field cannot be made visible.");
                        }
                        return this;
                    };
                }

                return oSearchield;
            }
        }

        /**
         * Shows or hides the search field.
         * Note: Hiding the search field will also collapse it if it is expanded.
         *
         * @param  {boolean} bShow Whether to show or hide the search field.
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        setShowSearchField (...args) {
            if (this.#isShellBar()) {
                this.#oShellBar.setShowSearchField(...args);
            }
        }

        /**
         * Gets whether the search field is shown or hidden.
         *
         * @returns {boolean|undefined} True if the search field is shown, false if it is hidden, or undefined if not available.
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        getShowSearchField () {
            if (this.#isShellBar()) {
                return this.#oShellBar.getShowSearchField();
            }
        }

        /**
         * Binds the ShowSearchField property to a model. The model value must be compatible with
         * the value of @see #setShowSearchField.
         *
         * @param {string} sPath Absolute path to the property in the model.
         * @param {sap.ui.model.Model} oModel The model to bind to.
         * @since 1.142.0
         * @private
         * @ui5-restricted ANS
         */
        bindShowSearchField (sPath, oModel) {
            this.bindModelProperty(sPath, oModel, "showSearchField", this.setShowSearchField.bind(this));
        }

        /**
         * Unbinds the ShowSearchField property from a model.
         *
         * @since 1.142.0
         * @private
         * @ui5-restricted ANS
         */
        unbindShowSearchField () {
            this.unbindModelProperty("showSearchField");
        }

        /**
         * Attaches a handler to the search button press event.
         * Fired, when the search button is activated.
         * Note: You can prevent expanding/collapsing of the search field by calling <code>event.preventDefault()</code>.
         *
         * @param {object} [oData] An object that will be passed to the handler along with the event object when the event is fired
         * @param {function} fnFunction The handler function to call when the event occurs.
         * This function will be called in the context of the oListener instance (if present)
         * or on the event provider instance. The event object (@see: sap.ui.base.Event) is provided
         * as first argument of the handler. Handlers must not change the content of the event.
         * The second argument is the specified oData instance (if present).
         * @param {object} [oListener] The object that wants to be notified when the event occurs (this context within the handler function).
         *
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        attachSearchButtonPress (...args) {
            if (this.#isShellBar()) {
                this.#oShellBar.attachSearchButtonClick(...args);
            } else {
                this.#getShellHeaderSearchButton()?.attachPress(...args);
            }
        }

        /**
         * Detaches a handler from the search button press event.
         *
         * @param {function} fnFunction The handler function to detach from the event.
         * @param {object} [oListener] The object that wanted to be notified when the event occurred.
         *
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        detachSearchButtonPress (...args) {
            if (this.#isShellBar()) {
                this.#oShellBar.detachSearchButtonClick(...args);
            } else {
                this.#getShellHeaderSearchButton()?.detachPress(...args);
            }
        }

        /**
         * Detaches a handler from the search button toggle event.
         * Fired, when the search field is expanded or collapsed.
         * Note: this event is only available when using the ShellBar.
         *
         * @param {object} [oData] An object that will be passed to the handler along with the event object when the event is fired
         * @param {function} fnFunction The handler function to call when the event occurs.
         * This function will be called in the context of the oListener instance (if present)
         * or on the event provider instance. The event object (@see: sap.ui.base.Event) is provided
         * as first argument of the handler. Handlers must not change the content of the event.
         * The second argument is the specified oData instance (if present).
         * @param {object} [oListener] The object that wants to be notified when the event occurs (this context within the handler function).
         *
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        attachSearchFieldToggle (...args) {
            if (this.#isShellBar()) {
                this.#oShellBar.attachSearchFieldToggle(...args);
            }
        }

        /**
         * Detaches a handler from the search field toggle event.
         *
         * @param {function} fnFunction The handler function to detach from the event.
         * @param {object} [oListener] The object that wanted to be notified when the event occurred.
         *
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        detachSearchFieldToggle (...args) {
            if (this.#isShellBar()) {
                this.#oShellBar.detachSearchFieldToggle(...args);
            }
        }

        /**
         * Attaches a handler to the search field clear event.
         * Fired, when the search cancel button is activated.
         * Note: You can prevent the default behavior (clearing the search field value)
         * by calling <code>event.preventDefault()</code>. The search will still be closed.
         * Note: this event is only available when using the ShellBar.
         *
         * @param {object} [oData] An object that will be passed to the handler along with the event object when the event is fired
         * @param {function} fnFunction The handler function to call when the event occurs.
         * This function will be called in the context of the oListener instance (if present)
         * or on the event provider instance. The event object (@see: sap.ui.base.Event) is provided
         * as first argument of the handler. Handlers must not change the content of the event.
         * The second argument is the specified oData instance (if present).
         * @param {object} [oListener] The object that wants to be notified when the event occurs (this context within the handler function).
         *
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        attachSearchFieldClear (...args) {
            if (this.#isShellBar()) {
                this.#oShellBar.attachSearchFieldClear(...args);
            }
        }

        /**
         * Detaches a handler from the search field clear event.
         *
         * @param {function} fnFunction The handler function to detach from the event.
         * @param {object} [oListener] The object that wanted to be notified when the event occurred.
         *
         * @since 1.142.0
         * @private
         * @ui5-restricted HAN-AS-INA-UI
         */
        detachSearchFieldClear (...args) {
            if (this.#isShellBar()) {
                this.#oShellBar.detachSearchFieldClear(...args);
            }
        }
    }

    return SearchAPI;
});
