// #region Imports
import { RendererServiceLocator } from '../../../../Dom/Renderer';
import { on, waitForEvent } from '@breadstone/mosaik-elements';
import { EID } from '../../../../Utilities/EId';
import { Appearance } from '../../../Types/Appearance';
import { MessageBoxButtons } from '../../../Types/MessageBoxButtons';
import { Variant } from '../../../Types/Variant';
import { PortalElement } from '../../Primitives/Portal/PortalElement';
import { MessageBoxElement } from './MessageBoxElement';
/**
 * Represents the `MESSAGE_BOX_DEFAULT_OPTIONS` constant.
 *
 * @private
 */
const MESSAGE_BOX_DEFAULT_OPTIONS = {
    header: '',
    subHeader: '',
    message: '',
    appearance: Appearance.Default,
    variant: Variant.Default,
    labels: {
        ok: 'OK',
        cancel: 'Cancel',
        yes: 'Yes',
        no: 'No'
    },
    clickOutsideToClose: false,
    pressEscapeToClose: false,
    closeable: false,
    hasBackdrop: true,
    isOpen: true,
    buttons: MessageBoxButtons.OK,
    icon: ''
};
/**
 * Service for managing the display and behavior of message box elements.
 * Provides methods to open and close message box elements within a portal structure.
 *
 * @public
 */
export class MessageBoxService {
    // #region Fields
    _messageBoxMap;
    _portalElement;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `MessageBoxService` class.
     *
     * @public
     */
    constructor() {
        this._messageBoxMap = new Map();
        on(window, 'popstate', () => {
            if (this._messageBoxMap.size > 0) {
                for (const [key, value] of this._messageBoxMap) {
                    if (value.options?.navigateToClose) {
                        void this.close(key);
                    }
                }
            }
        });
    }
    // #endregion
    // #region Methods
    /**
     * Opens a custom wrapper component containing a message box within a portal.
     *
     * @public
     * @param {string} messageBoxId - Unique identifier for the message box instance.
     * @param {IMessageBoxOptions} options - Properties for portal and message box.
     * @returns {Promise<MessageBoxResult>} A promise resolving with the result of the message box.
     */
    async open(messageBoxId, options) {
        let ref = this._messageBoxMap.get(messageBoxId);
        if (!ref) {
            const messageBoxElement = RendererServiceLocator.current.createElement(MessageBoxElement.is);
            RendererServiceLocator.current.setProperty(messageBoxElement, 'id', messageBoxId);
            const portalProjection = await this.extendToPortal(messageBoxElement, options);
            // eslint-disable-next-line require-atomic-updates
            this._messageBoxMap.set(messageBoxId, ref = {
                messageBoxId: messageBoxId,
                messageBoxElement: messageBoxElement,
                portalProjectionElement: portalProjection,
                options: options
            });
        }
        Object.assign(ref.messageBoxElement, {
            ...MESSAGE_BOX_DEFAULT_OPTIONS,
            ...options
        });
        return new Promise((resolve) => {
            ref.resolve = resolve;
            ref.messageBoxElement.open();
            void Promise.all([
                waitForEvent(ref.messageBoxElement, 'closed')
            ]).then(() => {
                this._messageBoxMap.delete(messageBoxId);
                ref.portalProjectionElement.close();
                ref.portalProjectionElement.remove();
            });
        });
    }
    /**
     * Closes the message box in the wrapper component and optionally removes the wrapper from the DOM.
     *
     * @public
     * @param {string} messageBoxId - Unique identifier for the message box instance.
     * @returns {Promise<void>} A promise resolving once the message box is fully closed.
     */
    async close(messageBoxId) {
        return new Promise((resolve) => {
            const ref = this._messageBoxMap.get(messageBoxId);
            if (ref) {
                this._messageBoxMap.delete(messageBoxId);
                void waitForEvent(ref.messageBoxElement, 'dialogResult').then((event) => {
                    ref.portalProjectionElement.close();
                    ref.portalProjectionElement.remove();
                    if (ref.resolve) {
                        ref.resolve(event.detail.result);
                    }
                    resolve();
                });
                ref.messageBoxElement.close();
            }
            else {
                console.error(`Wrapper with ID "${messageBoxId}" not found.`);
                resolve(); // Resolve to avoid hanging promise if message box not found
            }
        });
    }
    /**
     * Extends the message box component by placing it within a portal structure.
     *
     * @private
     * @param portalProps - Properties to set on the portal.
     * @param element - The element to place within the portal.
     * @returns The created PortalElement containing the message box.
     */
    extendToPortal(element, props) {
        return new Promise((resolve) => {
            if (!this._portalElement) {
                this._portalElement = RendererServiceLocator.current.createElement(PortalElement.is);
                RendererServiceLocator.current.setAttribute(this._portalElement, 'id', EID.next(PortalElement.is));
                Object.assign(this._portalElement, props);
                this._portalElement.addEventListener('portalAttached', (e) => {
                    resolve(e.detail.projection);
                });
                RendererServiceLocator.current.appendChild(document.body, this._portalElement);
            }
            RendererServiceLocator.current.appendChild(this._portalElement, element);
        });
    }
}
/**
 * @public
 */
export class MessageBoxServiceLocator {
    // #region Fields
    static _current = null;
    // #endregion
    // #region Properties
    static get current() {
        if (!MessageBoxServiceLocator.isSet()) {
            throw new Error('\'MessageBoxService\' not set.');
        }
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        return MessageBoxServiceLocator._current;
    }
    // #endregion
    // #region Methods
    static isSet() {
        return MessageBoxServiceLocator._current !== null;
    }
    static set(current) {
        MessageBoxServiceLocator._current = current;
    }
}
/**
 * @public
 */
MessageBoxServiceLocator.set(new MessageBoxService());
//# sourceMappingURL=MessageBoxService.js.map