import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
import React, { createRef, forwardRef, useEffect, useMemo, useRef, useState, } from "react";
import Styles from "./Transition.module.scss";
function TransitionClasses(type) {
    switch (type) {
        case TransitionAnimationTypes.COIN_FLIP:
            return {
                backward: {
                    elementExiting: Styles.flipBottomUpOut,
                    elementEntering: Styles.flipBottomUpIn,
                },
                forward: {
                    elementExiting: Styles.flipBottomUpOut,
                    elementEntering: Styles.flipBottomUpIn,
                },
            };
        case TransitionAnimationTypes.FADE:
            return {
                backward: {
                    elementExiting: Styles.fadeOutAbsolute,
                    elementEntering: Styles.fadeInDelayed,
                },
                forward: {
                    elementExiting: Styles.fadeOutAbsolute,
                    elementEntering: Styles.fadeInDelayed,
                },
            };
        case TransitionAnimationTypes.SLIDE:
            return {
                backward: {
                    elementEntering: Styles.entranceLeft,
                    elementExiting: "",
                },
                forward: {
                    elementEntering: "",
                    elementExiting: Styles.exitLeft,
                },
            };
        case TransitionAnimationTypes.POP_FROM_CLICK_ORIGIN:
        case TransitionAnimationTypes.POP_FROM_ELEMENT_ID:
            return {
                backward: {
                    elementEntering: Styles.fadeIn,
                    elementExiting: Styles.scaleOut,
                },
                forward: {
                    elementEntering: Styles.scaleIn,
                    elementExiting: Styles.fadeOut,
                },
            };
        case TransitionAnimationTypes.MASK:
            return {
                backward: {
                    elementEntering: Styles.stubEntering,
                    elementExiting: Styles.stub,
                },
                forward: {
                    elementEntering: Styles.stubEntering,
                    elementExiting: Styles.stub,
                },
            };
    }
}
function ChildrenWrapperFactory(func, key, externalPropsContainer, 
/** This is used to prevent newer "duplicated" keys from being removed after the new element finished animation */
timestamp, toRemoveKeys) {
    func.createdAt = timestamp;
    func.associatedKey = key;
    func.toRemoveKeys = toRemoveKeys;
    func.externalProps = externalPropsContainer;
    return func;
}
function _Transition({ step, contentStyle, contentClassName = "", children, className, onDiscardStep, lockTransitionWidth = false, lockTransitionHeight = false, ...props }, _containerRef) {
    const containerRef = useMemo(() => _containerRef || createRef(), [_containerRef]);
    const preTransitionDetails = useRef({
        transformOrigin: "initial",
    });
    const testId = props["data-testid"] ?? "transition-container";
    const [childrenWrappers, setChildrenWrappers] = useState(() => {
        const externalProps = {
            "data-testid": testId,
            key: String(children[step]?.key || step),
            style: contentStyle,
            className: contentClassName,
        };
        const func = ChildrenWrapperFactory(({ children }) => _jsx("div", { ...externalProps, children: children }), children[step].key || step, externalProps, Date.now(), []);
        return [func];
    });
    const prevStep = useRef(step);
    const prevChild = useRef(children[step]);
    useEffect(() => () => {
        prevChild.current = children[step];
    });
    useEffect(() => {
        containerRef.current.style.overflow = "hidden";
        const transitionClasses = props.transitionType === TransitionAnimationTypes.CUSTOM
            ? props.config
            : TransitionClasses(props.transitionType || TransitionAnimationTypes.SLIDE);
        if (prevChild.current !== null &&
            children[step]?.key === prevChild.current?.key // I'm rendering the same screen
        ) {
            return () => {
                prevStep.current = step;
            };
        }
        const key = children[step]?.key || step;
        if (prevStep.current !== step && lockTransitionWidth)
            containerRef.current.style.width = `${containerRef.current.clientWidth}px`;
        if (prevStep.current !== step && lockTransitionHeight)
            containerRef.current.style.height = `${containerRef.current.clientHeight}px`;
        const transitionMask = props.transitionType === TransitionAnimationTypes.MASK
            ? props.maskFactory(containerRef.current)
            : "";
        const insertedAt = Date.now();
        /** This runs on backwards */
        if (prevStep.current > step) {
            const stepToRemove = prevStep.current;
            const prevKeyToRemove = prevChild.current?.key || stepToRemove;
            setChildrenWrappers((screensBeforeChangingStep) => {
                const enteringScreenRef = createRef();
                const [FirstNextScreen, ...restOfScreens] = screensBeforeChangingStep.filter((a) => a?.associatedKey !== String(key));
                if (FirstNextScreen) {
                    Object.assign(FirstNextScreen.externalProps, {
                        "data-testid": testId,
                        style: { ...contentStyle, WebkitMaskImage: transitionMask },
                        className: `${transitionClasses.backward.elementExiting}`,
                    });
                }
                function animationEndListener(event) {
                    if (event.target !== event.currentTarget)
                        return;
                    const isAnimationFromExpectedState = enteringScreenRef.current?.classList.contains(transitionClasses.backward.elementEntering);
                    enteringScreenRef.current?.classList.remove(transitionClasses.backward.elementEntering);
                    setChildrenWrappers((screensAfterTheCurrentStepEntered) => {
                        if (onDiscardStep) {
                            if (isAnimationFromExpectedState)
                                onDiscardStep(prevKeyToRemove, insertedAt);
                            if (FirstNextScreen && FirstNextScreen.toRemoveKeys)
                                FirstNextScreen.toRemoveKeys.forEach(onDiscardStep);
                        }
                        return screensAfterTheCurrentStepEntered.filter((s) => {
                            const shouldKeep = (s?.associatedKey !== String(prevKeyToRemove) &&
                                !FirstNextScreen?.toRemoveKeys?.some((k) => String(k) === String(s?.associatedKey))) ||
                                s.createdAt > insertedAt;
                            return shouldKeep;
                        });
                    });
                    event.currentTarget.removeEventListener("animationend", animationEndListener);
                }
                const propsContainer = {
                    ref: enteringScreenRef,
                    "data-testid": testId,
                    key,
                    className: `${transitionClasses.backward.elementEntering} ${contentClassName}`,
                    style: {
                        ...contentStyle,
                    },
                    onAnimationEnd: animationEndListener,
                };
                const newWrapper = ChildrenWrapperFactory(({ children }) => {
                    return _jsx("div", { ...propsContainer, children: children });
                }, key, propsContainer, insertedAt, [prevKeyToRemove, ...(FirstNextScreen?.toRemoveKeys || [])]);
                if (FirstNextScreen)
                    return [
                        newWrapper,
                        FirstNextScreen ||
                            ChildrenWrapperFactory(() => _jsx(React.Fragment, {}), "", {}, insertedAt),
                        ...restOfScreens,
                    ];
                else
                    return [newWrapper];
            });
        }
        else if (prevStep.current < step) {
            const stepToDelete = prevStep.current;
            const prevKeyToRemove = String(prevChild.current?.key || stepToDelete);
            setChildrenWrappers((screensBeforeChangingStep) => {
                const lastIndex = screensBeforeChangingStep.length - 1;
                const lastWrapper = screensBeforeChangingStep[lastIndex];
                const nextScreenRef = createRef();
                if (lastWrapper) {
                    Object.assign(lastWrapper.externalProps, {
                        "data-testid": testId,
                        style: { ...contentStyle, WebkitMaskImage: transitionMask },
                        className: `${contentClassName} ${transitionClasses.forward.elementExiting}`,
                        onAnimationEnd: (e) => {
                            if (e.target !== e.currentTarget)
                                return;
                            if (transitionClasses.forward.elementEntering)
                                nextScreenRef.current?.classList.remove(transitionClasses.forward.elementEntering);
                            if (onDiscardStep)
                                onDiscardStep(prevKeyToRemove, insertedAt);
                            setChildrenWrappers((screensAfterTheCurrentStepEntered) => {
                                const nextState = screensAfterTheCurrentStepEntered.filter((s) => {
                                    const shouldMantain = s?.associatedKey !== String(prevKeyToRemove) ||
                                        s.createdAt > insertedAt;
                                    return shouldMantain;
                                });
                                return nextState;
                            });
                        },
                    });
                }
                const propsContainer = {
                    ref: nextScreenRef,
                    "data-testid": testId,
                    key,
                    style: {
                        ...contentStyle,
                    },
                    className: `${contentClassName} ${transitionClasses.forward.elementEntering}`,
                };
                const newWrapper = ChildrenWrapperFactory(({ children }) => {
                    return _jsx("div", { ...propsContainer, children: children });
                }, key, propsContainer, insertedAt);
                return [
                    ...screensBeforeChangingStep.slice(0, lastIndex),
                    lastWrapper ||
                        ChildrenWrapperFactory(() => _jsx(React.Fragment, {}), "fallback", {}, insertedAt),
                    newWrapper,
                ];
            });
        }
        return () => {
            prevStep.current = step;
        };
    }, [step]);
    useEffect(() => {
        if (childrenWrappers.length === 1 && lockTransitionWidth)
            containerRef.current.style.width = "";
        if (childrenWrappers.length === 1 && lockTransitionHeight)
            containerRef.current.style.height = "";
    }, [childrenWrappers.length !== 1]);
    useEffect(() => {
        if (props.transitionType === TransitionAnimationTypes.POP_FROM_ELEMENT_ID &&
            "elementId" in props) {
            const element = document.querySelector(`#${props.elementId}`);
            if (element) {
                const { clientWidth, clientHeight } = containerRef.current;
                const offsetX = element.clientLeft + element.offsetLeft + element.clientWidth / 2;
                const offsetY = element.clientTop + element.offsetTop + element.clientHeight / 2;
                const percentX = (offsetX * 100) / clientWidth;
                const percentY = (offsetY * 100) / clientHeight;
                preTransitionDetails.current.transformOrigin = `${percentX}% ${percentY}%`;
                Object.assign(containerRef.current.style, {
                    transformOrigin: `${percentX}% ${percentY}%`,
                });
            }
        }
        else {
            Object.assign(containerRef.current.style, preTransitionDetails.current);
        }
        if (childrenWrappers.length === 1) {
            containerRef.current.style.overflow = "";
        }
    }, [childrenWrappers.length]);
    const propsToSpread = { ...props };
    delete propsToSpread.transitionType;
    delete propsToSpread.config;
    return (_jsx(_Fragment, { children: _jsx("section", { ...propsToSpread, onClick: (e) => {
                const { currentTarget: { offsetTop, offsetLeft, clientWidth, clientHeight }, clientX, clientY, } = e;
                const offsetX = clientX - offsetLeft;
                const offsetY = clientY - offsetTop;
                const percentX = (offsetX * 100) / clientWidth;
                const percentY = (offsetY * 100) / clientHeight;
                if (props.transitionType ===
                    TransitionAnimationTypes.POP_FROM_ELEMENT_ID ||
                    props.transitionType ===
                        TransitionAnimationTypes.POP_FROM_CLICK_ORIGIN)
                    preTransitionDetails.current.transformOrigin = `${percentX}% ${percentY}%`;
                if (props.onClick)
                    props.onClick(e);
            }, "data-testid": "transition-controller", ref: containerRef, className: `${Styles.section} ${className}`, children: childrenWrappers.map((Wrapper) => {
                const childToRender = children.find((a, i) => (a?.key || i) === Wrapper?.associatedKey);
                return (_jsx(Wrapper, { children: _jsx(_Fragment, { children: childToRender }) }, `${Wrapper.associatedKey} ${Wrapper.createdAt}`));
            }) }) }));
}
export var TransitionAnimationTypes;
(function (TransitionAnimationTypes) {
    TransitionAnimationTypes[TransitionAnimationTypes["SLIDE"] = 0] = "SLIDE";
    TransitionAnimationTypes[TransitionAnimationTypes["POP_FROM_CLICK_ORIGIN"] = 1] = "POP_FROM_CLICK_ORIGIN";
    TransitionAnimationTypes[TransitionAnimationTypes["POP_FROM_ELEMENT_ID"] = 2] = "POP_FROM_ELEMENT_ID";
    TransitionAnimationTypes[TransitionAnimationTypes["FADE"] = 3] = "FADE";
    TransitionAnimationTypes[TransitionAnimationTypes["COIN_FLIP"] = 4] = "COIN_FLIP";
    TransitionAnimationTypes[TransitionAnimationTypes["MASK"] = 5] = "MASK";
    TransitionAnimationTypes[TransitionAnimationTypes["CUSTOM"] = 6] = "CUSTOM";
})(TransitionAnimationTypes || (TransitionAnimationTypes = {}));
/**
 * A component that manages smooth transitions between different views or screens.
 * It handles animations and switching between multiple children elements.
 *
 * Simple explanation: This component controls how content changes from one screen to another,
 * making the transition look smooth and professional with various animation styles.
 */
const Transition = forwardRef(_Transition);
/**
 * A component that manages smooth transitions between different views or screens.
 * It handles animations and switching between multiple children elements.
 */
export default Transition;
//# sourceMappingURL=Transition.js.map