import { createElement as _createElement } from "react";
import { jsxs as _jsxs, Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
import { useContext, useReducer, useMemo, useLayoutEffect, useRef, useState, forwardRef, useEffect } from 'react';
import { createPortal } from 'react-dom';
import styled, { css } from 'styled-components';
import Button from '../Button';
import Icon, { registerIcon } from '../Icon';
import Link, { StyledLink } from '../Link';
import * as timesIcon from '../Icon/icons/times.icon';
import { createUID, windowIsAvailable, withTestIds, hasProp, getActiveElement, getFocusables } from '../../utils';
import { useConfiguration, useI18n, useLiveLog, useOS, useShortcut, useTestIds, useConsolidatedRef, useEscape, useFocusWithin } from '../../hooks';
import { defaultThemeProp } from '../../theme';
import Flex from '../Flex';
import '../../init';
import { registerAction } from '../ShortcutManager';
import { Keyboard } from '../Badges';
import { StyledKeyboard } from '../Badges/Keyboard';
import { ToasterContext } from './Context';
import { getToasterTestIds } from './Toaster.test-ids';
registerIcon(timesIcon);
registerAction({ id: 'JumpToToast', defaultKeyBinding: 'Alt+Enter' }, { id: 'DismissToast', defaultKeyBinding: 'Alt+Q' });
const StyledToastButton = styled.button(({ theme }) => css `
    border: none;
    color: inherit;
    padding: 0;
    font-size: 1rem;
    margin-block: calc(${theme.base.spacing} / 2);
    margin-inline-end: calc(${theme.base.spacing} / 2);

    &:enabled:hover {
      background: #ffffff19;
    }

    &:enabled:focus {
      box-shadow: ${theme.base.shadow.focus};
    }
  `);
StyledToastButton.defaultProps = defaultThemeProp;
const StyledToastContent = styled.div(({ theme }) => {
    return css `
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: clip;
    text-overflow: ellipsis;
    font-size: ${theme.base['font-size']};
    margin-inline-start: calc(${theme.base['border-radius']} / 2);
    padding-inline-start: calc(${theme.base.spacing} / 2);

    ${StyledLink} {
      color: ${theme.base.colors.blue.light};
    }
  `;
});
StyledToastContent.defaultProps = defaultThemeProp;
const StyledToast = styled.div(({ theme }) => {
    return css `
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    width: 100%;
    background: ${theme.base.colors.slate['extra-dark']};
    color: ${theme.base.colors.white};
    border-radius: calc(${theme.base['border-radius']} / 2);
    opacity: var(--opacity);
    transform: translateY(var(--translateY)) scale(var(--scale));
    transition-property: transform, opacity;
    transition-duration: ${theme.base.animation.speed};
    transition-timing-function: ${theme.base.animation.timing.ease};
    font-weight: ${theme.base['font-weight']['semi-bold']};

    :focus {
      box-shadow: ${theme.base.shadow.focus};
    }

    ${StyledKeyboard} {
      color: ${theme.base.palette.light};
      background-color: ${theme.base.colors.gray.dark};
      border-color: ${theme.base.colors.gray.medium};
      inset-block-start: unset;
    }
  `;
});
StyledToast.defaultProps = defaultThemeProp;
const StyledToaster = styled.div(({ theme }) => {
    return css `
    position: fixed;
    z-index: ${theme.base['z-index'].toast};
    width: min(40ch, calc(100% - 1rem));
    inset-inline-start: 50%;
    transform: translateX(-50%);
    top: 100%;
  `;
});
StyledToaster.defaultProps = defaultThemeProp;
function reducer(current, action) {
    switch (action.type) {
        case 'push':
            return [
                ...current,
                {
                    ...action.payload
                }
            ];
        case 'unmount':
            return current.filter(m => m.id !== action.payload);
        default:
            return current;
    }
}
const Toast = forwardRef(function Toast({ id, firstToast, dispatch, topLevelDismiss, translateY, onDismiss, dismissAfter, ...restProps }, ref) {
    const [dismissed, setDismissed] = useState(false);
    const timeoutRef = useRef(NaN);
    const t = useI18n();
    const { macintosh } = useOS();
    const { announcePolite } = useLiveLog();
    const content = hasProp(restProps, 'href') ? t('created') : restProps.content;
    const initiatorRef = useRef(null);
    const containerRef = useConsolidatedRef(ref);
    useEffect(() => {
        const fullMessage = hasProp(restProps, 'href')
            ? `${content} ${id} ${restProps.label}`
            : content;
        const shortcutJumpMessage = t('keypress_instruction', [
            macintosh ? 'Option + Enter' : 'Alt + Enter',
            t('shortcut_JumpToToast')
        ]);
        const shortcutDismissMessage = t('keypress_instruction', [
            macintosh ? 'Option + Q' : 'Alt + Q',
            t('shortcut_DismissToast')
        ]);
        announcePolite({
            message: `${fullMessage}. ${shortcutJumpMessage}. ${shortcutDismissMessage}.`,
            type: 'acknowledgement'
        });
    }, []);
    const onTransitionEnd = (e) => {
        if (dismissed && e.propertyName === 'opacity' && e.target === e.currentTarget) {
            dispatch({ type: 'unmount', payload: id });
        }
    };
    const dismiss = (timeout) => () => {
        if (initiatorRef.current) {
            initiatorRef.current?.focus?.();
            initiatorRef.current = null;
        }
        clearTimeout(timeoutRef.current);
        if (dismissed)
            return;
        setDismissed(true);
        onDismiss?.({ id, timeout });
        topLevelDismiss?.({ id, timeout });
    };
    const startTimer = () => {
        if (Number.isFinite(dismissAfter)) {
            timeoutRef.current = window.setTimeout(dismiss(true), dismissAfter);
        }
    };
    useEffect(() => {
        startTimer();
        return () => {
            window.clearTimeout(timeoutRef.current);
        };
    }, []);
    // Alt+Enter focuses on the first focusable element in the first toast.
    // Tab to the next toast if there are multiple.
    useShortcut('JumpToToast', () => {
        const activeElement = getActiveElement();
        if (activeElement && activeElement !== document.body) {
            initiatorRef.current = activeElement;
        }
        if (firstToast && containerRef.current) {
            getFocusables(containerRef.current)[0]?.focus();
        }
    });
    // Alt+Q dismisses the first toast.
    useShortcut('DismissToast', () => {
        if (firstToast)
            dismiss(false)();
    });
    // Escape dismisses the toast and focuses the initiator.
    useEscape(() => {
        const currentFocus = getActiveElement();
        if (containerRef.current?.contains(currentFocus)) {
            dismiss(false)();
        }
    });
    useFocusWithin([containerRef], (focused) => {
        if (focused) {
            window.clearTimeout(timeoutRef.current);
        }
        else {
            startTimer();
        }
    });
    return (_jsxs(Flex, { ...restProps, "data-app-region": true, container: { alignItems: 'center', gap: 0.25 }, as: StyledToast, ref: containerRef, style: {
            '--translateY': `${-translateY}px`,
            '--opacity': dismissed ? '0' : '1',
            '--scale': dismissed ? '0' : '1'
        }, onTransitionEnd: onTransitionEnd, children: [_jsxs(Flex, { item: { grow: 1 }, as: StyledToastContent, children: [content, hasProp(restProps, 'href') ? (_jsxs(_Fragment, { children: [' ', _jsxs(Link, { href: restProps.href, children: ["(", id, "): ", restProps.label] })] })) : undefined] }), _jsxs(Flex, { container: { inline: true, justify: 'end', gap: 0.5 }, children: [_jsx(Keyboard, { keyName: 'Alt' }), _jsx(Keyboard, { keyName: 'Enter' })] }), _jsx(Button, { as: StyledToastButton, variant: 'simple', onClick: dismiss(false), icon: true, "aria-label": t('dismiss_label'), children: _jsx(Icon, { name: 'times' }) })] }));
});
const Toaster = ({ testId, children, dismissAfter = Infinity, onDismiss }) => {
    const { portalTarget } = useConfiguration();
    const [messages, dispatch] = useReducer(reducer, []);
    const [yOffsets, setYOffsets] = useState([]);
    const heightHashRef = useRef({});
    const testIds = useTestIds(testId, getToasterTestIds);
    // This effect will cause a re-render with a translateY value set for each toast.
    useLayoutEffect(() => {
        const heights = [...messages].reverse().map(({ id }) => heightHashRef.current[id]);
        setYOffsets(heights
            .map((_, i) => heights.slice(0, i + 1).reduce((sum, cur) => sum + cur + 4, 0))
            .reverse());
    }, [messages.length]);
    const providerValue = useMemo(() => ({
        push(incoming) {
            const message = {
                ...incoming,
                id: incoming.id ?? createUID(),
                dismissAfter: hasProp(incoming, 'href') ? 10000 : incoming.dismissAfter
            };
            dispatch({
                type: 'push',
                payload: message
            });
        },
        initialized: true
    }), []);
    // Don't create additional ToasterContext.Providers.
    const context = windowIsAvailable
        ? window.cosmos.toasterContext ?? ToasterContext
        : ToasterContext;
    const priorCtx = useContext(context);
    if (priorCtx.initialized) {
        return _jsx(context.Provider, { value: priorCtx, children: children });
    }
    return (_jsxs(_Fragment, { children: [_jsx(context.Provider, { value: providerValue, children: children }), !!messages.length &&
                portalTarget &&
                createPortal(_jsx(StyledToaster, { "data-testid": testIds.root, children: messages.map((message, i) => {
                        const sharedProps = {
                            translateY: yOffsets[i] ?? 0,
                            firstToast: i === 0,
                            topLevelDismiss: onDismiss,
                            dispatch,
                            ref: el => {
                                if (el) {
                                    heightHashRef.current[message.id] = el.offsetHeight;
                                }
                                else {
                                    delete heightHashRef.current[message.id];
                                }
                            }
                        };
                        if (hasProp(message, 'href')) {
                            return (
                            // Toast with Link
                            _createElement(Toast, { "data-testid": testIds.toast, ...message, ...sharedProps, key: message.id }));
                        }
                        return (
                        // Base Toast
                        _createElement(Toast, { "data-testid": testIds.toast, ...message, ...sharedProps, dismissAfter: message.dismissAfter ?? dismissAfter, key: message.id }));
                    }) }), portalTarget)] }));
};
export default withTestIds(Toaster, getToasterTestIds);
//# sourceMappingURL=Toaster.js.map