import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { forwardRef, useState, useCallback, useEffect, useMemo, useContext, useRef } from 'react';
import Flex from '../Flex';
import Link from '../Link';
import Icon, { registerIcon } from '../Icon';
import * as timesIcon from '../Icon/icons/times.icon';
import * as plusIcon from '../Icon/icons/plus.icon';
import * as caseSolidIcon from '../Icon/icons/case-solid.icon';
import * as barsIcon from '../Icon/icons/bars.icon';
import * as caretLeftIcon from '../Icon/icons/caret-left.icon';
import * as caretRightIcon from '../Icon/icons/caret-right.icon';
import Image from '../Image';
import { useI18n, useBreakpoint, useConsolidatedRef, useDirection, useElement, useTransitionState, useEvent, useOuterEvent, useScrollToggle } from '../../hooks';
import { openCloseStates } from '../../hooks/useTransitionState';
import Grid from '../Grid';
import Button from '../Button';
import Drawer from '../Drawer';
import Backdrop from '../Backdrop';
import Tooltip from '../Tooltip/Tooltip';
import SkipLinks from '../SkipLinks';
import VisuallyHiddenText from '../VisuallyHiddenText';
import Progress from '../Progress';
import { registerAction } from '../ShortcutManager';
import useShortcut from '../../hooks/useShortcut';
import { focusHeadingOrContainer, isValidElement } from '../../utils';
import AppShellContext, { AppShellDrawerContext } from './AppShellContext';
import AppShellDrawer from './Drawer';
import AppHeader from './AppHeader';
import { StyledAppNav, StyledAppInfo, StyledCaseTypes, StyledScrollWrap, StyledUtils, StyledAppMain, StyledNavCasesList, StyledBannerRegion, StyledCountIcon, StyledUtilIconCount, StyledMobileNav, StyledAppShellTooltip, StyledAppShellToggleButton } from './AppShell.styles';
import NavigationList from './NavigationList';
import { AppShellSearch } from './AppShellSearch';
import { followingNodes } from './utils';
registerIcon(timesIcon, plusIcon, caseSolidIcon, barsIcon, caretRightIcon, caretLeftIcon);
registerAction({ id: 'NextRegion', defaultKeyBinding: 'F9' }, { id: 'PrevRegion', defaultKeyBinding: 'Shift+F9' });
const CaseTypes = ({ caseTypes }) => {
    const t = useI18n();
    const transformedCaseTypes = caseTypes.map(caseType => {
        return {
            ...caseType,
            primary: caseType.name
        };
    });
    return useMemo(() => (_jsx(StyledCaseTypes, { children: _jsx(NavigationList, { items: [
                {
                    primary: t('app_shell_create'),
                    visual: _jsx(Icon, { name: 'plus' }),
                    items: transformedCaseTypes
                }
            ] }) })), [caseTypes]);
};
const Links = (props) => {
    const { links } = props;
    const [transformedLinks, setTransformedLinks] = useState([]);
    // TODO: Can this be a memo instead of a state and an effect?
    const mergedNavLinks = (items) => {
        return items.map((link) => {
            return {
                ...link,
                primary: link.name,
                'aria-current': link.active ? 'page' : null,
                visual: link.icon ? _jsx(Icon, { name: link.icon }) : null,
                onClick: link.links ? undefined : link.onClick,
                items: link.links ? mergedNavLinks(link.links) : undefined
            };
        });
    };
    useEffect(() => {
        setTransformedLinks(mergedNavLinks(links));
    }, [links]);
    return (_jsx("div", { children: _jsx(NavigationList, { items: transformedLinks }) }));
};
const Cases = (props) => {
    const { cases } = props;
    const setupCases = (items) => {
        return items.map((item) => {
            const { id, primary, secondary, collapsedItemInfo, visual = _jsx(Icon, { name: 'case-solid' }), active, onClick, onDismiss, ...restProps } = item;
            return {
                ...restProps,
                id,
                'aria-current': active ? 'page' : undefined,
                primary,
                secondary,
                collapsedItemInfo,
                active: !!active,
                onClick,
                onDismiss,
                visual
            };
        });
    };
    return (_jsx(StyledNavCasesList, { children: _jsx(NavigationList, { items: setupCases(cases) }) }));
};
export const Utils = ({ contextSwitcher = {}, operator, items, appHeader }) => {
    const t = useI18n();
    const { contexts, onContextClick, label: contextSwitcherLabel = t('switch_to') } = contextSwitcher;
    const { navOpen, navState, drawerOpen, setDrawerOpen } = useContext(AppShellContext);
    const [item, setItem] = useState();
    useEffect(() => {
        if (drawerOpen && item) {
            setItem(items?.find((utilItem) => !isValidElement(utilItem) && utilItem.name === item.name));
        }
    }, [items]);
    const operatorInfo = useMemo(() => appHeader
        ? []
        : [
            {
                primary: t('app_shell_user_account'),
                visual: operator.avatar,
                items: operator.actions
            }
        ], [appHeader, operator.name, operator.avatar, operator.actions]);
    const contextSwitcherUtilItem = useMemo(() => {
        if (!appHeader && contexts && contexts?.length > 1 && !!onContextClick) {
            return {
                name: contextSwitcherLabel,
                primary: contextSwitcherLabel,
                visual: _jsx(Icon, { name: 'dot-9-solid' }),
                drawerView: (_jsx(NavigationList, { items: contexts.map(context => ({
                        ...context,
                        primary: context.primary,
                        'aria-current': !!context.selected,
                        visual: context.visual,
                        onClick: e => {
                            onContextClick(context.id, e);
                        }
                    })), singleSelect: true }))
            };
        }
    }, [appHeader, contexts, contextSwitcherLabel, onContextClick]);
    const transform = useCallback((utilItem) => {
        const uItem = { ...utilItem };
        delete uItem.onDrawerClose;
        delete uItem.onDrawerOpen;
        return {
            onClick: () => {
                if (utilItem.drawerView) {
                    setItem(utilItem);
                }
            },
            ...uItem,
            primary: utilItem.name,
            visual: utilItem.count ? (_jsxs(StyledUtilIconCount, { children: [utilItem.visual, _jsx(StyledCountIcon, { variant: 'urgent', id: `${utilItem.name}-count`, children: utilItem.count })] })) : (utilItem.visual),
            collapseItems: !navOpen
        };
    }, [navOpen]);
    const utilItems = useMemo(() => {
        return [
            ...(items ?? []).map(uItem => {
                return isValidElement(uItem) ? uItem : transform(uItem);
            }),
            ...(contextSwitcherUtilItem ? [transform(contextSwitcherUtilItem)] : []),
            ...operatorInfo
        ];
    }, [items, operatorInfo, transform]);
    useEffect(() => {
        if (item)
            setDrawerOpen(true);
    }, [item]);
    useEffect(() => {
        if (navState === 'closed' || navState === 'closing')
            setDrawerOpen(false);
    }, [navState]);
    return (_jsxs(AppShellDrawerContext.Provider, { value: useMemo(() => ({
            drawerOpen,
            closeDrawer: () => {
                setDrawerOpen(false);
            },
            openDrawer: setItem
        }), [drawerOpen]), children: [_jsx(StyledUtils, { children: _jsx(NavigationList, { items: utilItems, collapsed: true }) }), item && (_jsx(AppShellDrawer, { drawerOpen: drawerOpen, setDrawerOpen: setDrawerOpen, content: item.drawerView, header: item.drawerHeader || item.name, onDrawerClose: () => {
                    setItem(undefined);
                    item.onDrawerClose?.();
                }, onDrawerOpen: item.onDrawerOpen }))] }));
};
const AppShell = forwardRef(function AppShell(props, ref) {
    const t = useI18n();
    const { appInfo, contextSwitcher, searchInput, appHeader, searchPage, searchLabel = t('sitewide'), caseTypes, links, banners, progress, main, operator, utils, cases, defaultExpanded = false, collapsedHoverMenus = false, ...restProps } = props;
    const { appName, portalName, href: appInfoHref, onClick: appInfoOnClick, imageSrc: logoSrc, appNameHidden, ...appInfoRestProps } = appInfo;
    let appInfoAs = 'div';
    if (appInfoHref)
        appInfoAs = Link;
    else if (appInfoOnClick)
        appInfoAs = Button;
    const { start, end } = useDirection();
    const { disableScroll, enableScroll } = useScrollToggle();
    const navRef = useConsolidatedRef(ref);
    const [mobileNavOpen, setMobileNavOpen] = useState(false);
    const [mobileNavBackdrop, setMobileNavBackdrop] = useState(false);
    const [drawerOpen, setDrawerOpen] = useState(false);
    const [previewActive, setPreviewActive] = useState(false);
    const focusedImperatively = useRef(false);
    const [headerEl, setHeaderEl] = useElement();
    const [mobileHeaderEl, setMobileHeaderEl] = useElement();
    const [appInfoEl, setAppInfoEl] = useElement();
    const [expandCollapseEl, setExpandCollapseEl] = useElement();
    const isSmallOrAbove = useBreakpoint('sm');
    const showAppHeader = !!appHeader && isSmallOrAbove;
    const showMainContent = showAppHeader ? !!(headerEl || mobileHeaderEl) : true;
    const openNavTimeoutId = useRef();
    const closeNavTimeoutId = useRef();
    const previewTriggerRef = useRef(null);
    const [searchContainerEl, setSearchContainerEl] = useState(null);
    const { state: navState, set: setNavState } = useTransitionState({
        ref: navRef,
        property: 'width',
        states: openCloseStates,
        defaultState: defaultExpanded ? 'open' : 'closed'
    });
    const skipLinks = useMemo(() => [
        {
            label: t('go_to_main_content'),
            target: { ariaLabel: t('main_content'), selector: 'main' }
        }
    ], [t]);
    const openNav = useCallback((delay = 0) => {
        if (closeNavTimeoutId.current)
            clearTimeout(closeNavTimeoutId.current);
        openNavTimeoutId.current = setTimeout(() => {
            setNavState('open');
        }, delay);
    }, []);
    const closeNav = useCallback((delay = 0) => {
        if (openNavTimeoutId.current)
            clearTimeout(openNavTimeoutId.current);
        closeNavTimeoutId.current = setTimeout(() => {
            setNavState('closed');
        }, delay);
    }, []);
    const onFocus = useCallback(() => {
        focusedImperatively.current = false;
    }, [openNav, isSmallOrAbove]);
    const mobileNavRef = useRef(null);
    useOuterEvent('focusin', [mobileNavRef], () => setMobileNavOpen(false));
    useEffect(() => {
        if (mobileNavOpen) {
            disableScroll();
        }
        else {
            enableScroll();
        }
    }, [mobileNavOpen]);
    useEffect(() => {
        if (mobileNavOpen) {
            const onKeyDown = ({ key }) => {
                if (key === 'Escape')
                    setMobileNavOpen(false);
            };
            document.addEventListener('keydown', onKeyDown);
            return () => {
                document.removeEventListener('keydown', onKeyDown);
            };
        }
    }, [mobileNavOpen]);
    useEffect(() => {
        if (isSmallOrAbove && mobileNavOpen) {
            setMobileNavBackdrop(false);
            setMobileNavOpen(false);
        }
    }, [isSmallOrAbove, mobileNavOpen]);
    const activatedEl = useRef(document.body);
    useEvent('click', e => {
        activatedEl.current = document.elementFromPoint(e.clientX, e.clientY) ?? document.body;
    });
    useShortcut('NextRegion', () => {
        const activeElement = document.activeElement && document.activeElement !== document.body
            ? document.activeElement
            : activatedEl.current;
        const regionEls = followingNodes(document.createNodeIterator(document.body, NodeFilter.SHOW_ELEMENT, node => 
        // safe to treat as Element, as we requested to give us only elements
        node.hasAttribute('data-app-region') || node === activeElement
            ? NodeFilter.FILTER_ACCEPT
            : NodeFilter.FILTER_SKIP), activeElement);
        // try to focus first visibleregion
        const focusedNode = regionEls.find(node => focusHeadingOrContainer(node, ''));
        if (!focusedNode)
            activeElement.focus();
    });
    useShortcut('PrevRegion', () => {
        const activeElement = document.activeElement && document.activeElement !== document.body
            ? document.activeElement
            : activatedEl.current;
        const regionEls = followingNodes(document.createNodeIterator(document.body, NodeFilter.SHOW_ELEMENT, node => 
        // safe to treat as HTMLElement, as we requested to give us only elements
        node.hasAttribute('data-app-region') || node === activeElement
            ? NodeFilter.FILTER_ACCEPT
            : NodeFilter.FILTER_SKIP), activeElement);
        regionEls.reverse();
        if (regionEls[0].contains(activeElement))
            regionEls.splice(0, 1);
        const focusedNode = regionEls.find(node => focusHeadingOrContainer(node, ''));
        if (!focusedNode)
            activeElement.focus();
    });
    const appNav = (_jsxs(Flex, { ...restProps, container: {
            direction: 'column',
            justify: 'between'
        }, ref: isSmallOrAbove ? navRef : undefined, as: StyledAppNav, appHeader: showAppHeader, navOpen: mobileNavOpen, onFocus: onFocus, "data-app-region": true, "aria-label": t('app_shell_main_navigation_label'), children: [!showAppHeader && (_jsxs(_Fragment, { children: [_jsxs(StyledAppInfo, { as: Grid, forwardedAs: appInfoAs, variant: 'link', href: appInfoHref, onClick: appInfoOnClick, "aria-label": `${appName}${portalName ? ` - ${portalName}` : ''}`, ...appInfoRestProps, container: {
                            alignItems: 'center',
                            areas: portalName ? '"logo app" "logo portal"' : '"logo app"'
                        }, ref: setAppInfoEl, icon: true, children: [_jsx(Grid, { item: { area: 'logo' }, as: Image, src: logoSrc, alt: appName }), _jsx(Grid, { item: { area: 'app' }, as: appNameHidden ? VisuallyHiddenText : 'span', children: appName }), portalName && (_jsx(Grid, { item: { area: 'portal' }, as: 'span', children: portalName }))] }), navState === 'closed' && (!appNameHidden || portalName) && (_jsx(Tooltip, { target: appInfoEl, placement: 'right', as: StyledAppShellTooltip, showDelay: 'none', hideDelay: 'none', children: `${appNameHidden ? '' : appName} ${portalName ?? ''}`.trim() }))] })), searchInput && !showAppHeader && (_jsx(AppShellSearch, { searchLabel: searchLabel, collapsed: !mobileNavOpen && ['closed', 'closing'].includes(navState), searchInput: searchInput })), _jsxs(StyledScrollWrap, { tabIndex: -1, onClick: isSmallOrAbove
                    ? () => {
                        if (['closing', 'closed'].includes(navState)) {
                            openNav(0);
                        }
                        else {
                            closeNav(0);
                        }
                    }
                    : undefined, children: [caseTypes && caseTypes.length > 0 && _jsx(CaseTypes, { caseTypes: caseTypes }), links && _jsx(Links, { links: links }), cases && _jsx(Cases, { cases: cases })] }), _jsx(Utils, { appHeader: showAppHeader, operator: operator, contextSwitcher: contextSwitcher, items: utils }), !mobileNavOpen && (_jsxs(Flex, { container: { justify: 'center' }, children: [_jsx(StyledAppShellToggleButton, { icon: true, ref: setExpandCollapseEl, compact: true, variant: 'simple', onClick: () => {
                            setNavState(navState === 'closed' ? 'open' : 'closed');
                        }, "aria-label": navState === 'closed' ? t('expand_navigation') : t('collapse_navigation'), children: ['closed', 'closing'].includes(navState) ? (_jsx(Icon, { name: `arrow-micro-${end}` })) : (_jsx(Icon, { name: `arrow-micro-${start}` })) }), ['open', 'closed'].includes(navState) && (_jsx(Tooltip, { target: expandCollapseEl, placement: 'right', as: StyledAppShellTooltip, showDelay: 'none', hideDelay: 'none', children: navState === 'closed' ? t('expand_navigation') : t('collapse_navigation') }))] }))] }));
    return (_jsxs(AppShellContext.Provider, { value: useMemo(() => ({
            navOpen: mobileNavOpen || navState === 'open' || navState === 'opening',
            drawerOpen,
            setDrawerOpen,
            collapsedHoverMenus,
            navState,
            focusedImperatively,
            headerEl: headerEl || mobileHeaderEl,
            previewTriggerRef,
            searchContainerEl,
            setSearchContainerEl,
            mobileNavOpen,
            previewActive,
            setPreviewActive
        }), [
            navState,
            drawerOpen,
            mobileNavOpen,
            headerEl,
            mobileHeaderEl,
            searchContainerEl,
            previewActive
        ]), children: [_jsx(SkipLinks, { items: skipLinks }), showAppHeader && (_jsx(AppHeader, { ...appInfo, contextSwitcher: contextSwitcher, searchInput: searchInput, searchLabel: searchLabel, operator: operator, searchPage: searchPage, ref: setHeaderEl })), isSmallOrAbove ? (appNav) : (_jsxs(_Fragment, { children: [_jsx(Flex, { as: StyledMobileNav, container: { alignItems: 'center' }, ref: setMobileHeaderEl, children: _jsx(Button, { label: t('app_shell_open_nav'), variant: 'simple', onClick: () => setMobileNavBackdrop(true), "aria-label": t('app_shell_open_nav'), icon: true, children: _jsx(Icon, { name: 'bars' }) }) }), _jsx(Backdrop, { open: mobileNavBackdrop, transitionSpeed: 'none', onAfterTransitionIn: () => setMobileNavOpen(true), onBeforeTransitionOut: () => setMobileNavOpen(false), onClick: e => {
                            if (e.currentTarget === e.target)
                                setMobileNavOpen(false);
                        }, children: _jsx(Drawer, { open: mobileNavOpen, ref: mobileNavRef, onAfterClose: () => {
                                setMobileNavBackdrop(false);
                            }, position: 'fixed', placement: start, size: 'min(18.75rem, calc(100vw - 3.5rem))', nullWhenClosed: true, children: appNav }) })] })), showMainContent && (_jsxs(StyledAppMain, { appHeader: showAppHeader, mobileNav: !isSmallOrAbove, previewActive: previewActive, headerOffset: isSmallOrAbove ? headerEl?.offsetHeight : mobileHeaderEl?.offsetHeight, navOpen: ['open', 'opening'].includes(navState) || mobileNavOpen, "aria-busy": !!progress, "data-app-region": true, children: [_jsx(Progress, { visible: !!progress, placement: 'local', message: typeof progress === 'string' ? progress : t('loading') }), banners && _jsx(StyledBannerRegion, { children: banners }), main] }))] }));
});
export default AppShell;
//# sourceMappingURL=AppShell.js.map