import { createElement as _createElement } from "react";
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { useReducer, useMemo, useEffect, useContext, memo } from 'react';
import { createPortal } from 'react-dom';
import { createUID, getActiveElement, windowIsAvailable } from '../../utils';
import { useConfiguration, useScrollToggle } from '../../hooks';
import '../../init';
import DockedModals from './DockedModals';
import { ModalContext, ModalManagerContext } from './Contexts';
const reducer = (modals, payload) => {
    switch (payload.action) {
        case 'create':
            return [...modals, payload.modal];
        case 'unmount':
            return modals.filter(modal => modal.id !== payload.modal.id);
        case 'dismiss':
            return modals.map(modal => {
                modal.state = modal.id === payload.modal.id ? 'closed' : modal.state;
                return modal;
            });
        case 'update':
            return modals.map(modal => {
                modal.props =
                    modal.id === payload.modal.id ? { ...modal.props, ...payload.modal.props } : modal.props;
                return modal;
            });
        case 'activate': {
            const newModals = modals.map(modal => {
                modal.state = modal.id === payload.modal.id ? 'open' : modal.state;
                return modal;
            });
            // Move the newly activated modal to the end of the list
            // to ensure it is shown on top of other active modals.
            const activatedModal = newModals.splice(newModals.findIndex(modal => modal.id === payload.modal.id), 1);
            return [...newModals, ...activatedModal];
        }
        case 'minimize':
            return modals.map(modal => {
                modal.state =
                    modal.id === payload.modal.id && modal.options.minimizable ? 'minimized' : modal.state;
                return modal;
            });
        case 'maximize':
            return modals.map(modal => {
                modal.state =
                    modal.id === payload.modal.id && modal.options.maximizable ? 'maximized' : modal.state;
                return modal;
            });
        case 'dock':
            return modals.map(modal => {
                modal.state =
                    modal.id === payload.modal.id && modal.options.dockable ? 'docked' : 'minimized';
                return modal;
            });
        default:
            return modals;
    }
};
const WrappedModal = memo(({ Component, id, options: { alert, dismissible, dockable, minimizable, maximizable }, state, methods, props, top }) => (_jsx(ModalContext.Provider
// eslint-disable-next-line react/jsx-no-constructed-context-values
, { 
    // eslint-disable-next-line react/jsx-no-constructed-context-values
    value: {
        id,
        alert,
        dismissible,
        minimizable,
        maximizable,
        dockable,
        state,
        top,
        initialized: true,
        ...methods
    }, children: _jsx(Component, { ...props }) })));
const RenderModals = ({ modals }) => {
    const { open, minimized, alerts } = modals.reduce((obj, modal) => {
        if (modal.options.alert)
            obj.alerts.push(modal);
        else if (modal.state === 'minimized' || modal.state === 'docked') {
            obj.minimized.push(modal);
        }
        else
            obj.open.push(modal);
        return obj;
    }, { open: [], minimized: [], alerts: [] });
    return (_jsxs(_Fragment, { children: [open.map((modal, i) => (_createElement(WrappedModal, { ...modal, top: alerts.length === 0 && i === open.length - 1, key: modal.id }))), minimized.length > 0 && (_jsx(DockedModals, { children: minimized.map(modal => (_createElement(WrappedModal, { ...modal, top: false, key: modal.id }))) })), alerts.map((modal, i) => (_createElement(WrappedModal, { ...modal, top: i === open.length - 1, key: modal.id })))] }));
};
const ModalManager = ({ children }) => {
    const [modals, dispatch] = useReducer(reducer, []);
    const { disableScroll, enableScroll } = useScrollToggle();
    const providerValue = useMemo(() => ({
        create: (Component, props, options) => {
            const id = options?.id || createUID();
            const activeElement = getActiveElement();
            const initiatorRef = activeElement ? new WeakRef(activeElement) : null;
            const methods = {
                dismiss: () => {
                    dispatch({ action: 'dismiss', modal: { id } });
                    options?.onDismiss?.(id);
                    const initiator = initiatorRef?.deref();
                    const currentActiveElement = getActiveElement();
                    if (initiator?.isConnected &&
                        (initiator instanceof HTMLElement || initiator instanceof SVGElement) &&
                        currentActiveElement !== null &&
                        (currentActiveElement.closest('[aria-modal="true"]') !== null ||
                            currentActiveElement === document.body)) {
                        initiator.focus();
                    }
                },
                update: updateProps => {
                    dispatch({
                        action: 'update',
                        modal: { id, props: updateProps }
                    });
                },
                minimize: () => {
                    dispatch({ action: 'minimize', modal: { id } });
                    options?.onMinimize?.(id);
                },
                maximize: () => {
                    dispatch({ action: 'maximize', modal: { id } });
                    options?.onMaximize?.(id);
                },
                dock: () => {
                    dispatch({ action: 'dock', modal: { id } });
                    options?.onDock?.(id);
                },
                activate: () => {
                    dispatch({ action: 'activate', modal: { id } });
                    options?.onActivate?.(id);
                },
                unmount: () => {
                    dispatch({ action: 'unmount', modal: { id } });
                }
            };
            let defaultState;
            if (options?.defaultMinimized) {
                defaultState = 'minimized';
            }
            else if (options?.dockable) {
                defaultState = 'docked';
            }
            else {
                defaultState = 'open';
            }
            dispatch({
                action: 'create',
                modal: {
                    id,
                    methods,
                    Component,
                    props,
                    state: defaultState,
                    options: {
                        alert: false,
                        minimizable: false,
                        maximizable: false,
                        dockable: false,
                        defaultMinimized: false,
                        unmountWhenMinimized: true,
                        ...options,
                        dismissible: !!(options?.alert ? false : options?.dismissible ?? true)
                    }
                }
            });
            return methods;
        },
        ModalContext,
        initialized: true
    }), []);
    useEffect(() => {
        if (modals.some(modal => ['open', 'closed', 'maximized'].includes(modal.state))) {
            disableScroll();
        }
        else {
            enableScroll();
        }
    }, [modals]);
    const { portalTarget } = useConfiguration();
    const context = windowIsAvailable
        ? window.cosmos.modalManagerContext ?? ModalManagerContext
        : ModalManagerContext;
    const priorCtx = useContext(context);
    // Don't create additional Modal portals.
    if (priorCtx.initialized)
        return _jsx(context.Provider, { value: priorCtx, children: children });
    return (_jsxs(context.Provider, { value: providerValue, children: [children, modals.length > 0 &&
                portalTarget &&
                createPortal(_jsx(RenderModals, { modals: modals }), portalTarget)] }));
};
export default ModalManager;
//# sourceMappingURL=ModalManager.js.map