import { useEffect, useState } from "react";

import type { CSSProperties, FC, PropsWithChildren } from "react";

function getAnimationRelatedState({
    isVisible,
    animationIn,
    animationOut,
    animationInDuration,
    animationOutDuration,
    animationInDelay,
    animationOutDelay,
}: {
    isVisible: boolean;
    animationIn: string;
    animationOut: string;
    animationInDuration: number;
    animationOutDuration: number;
    animationInDelay: number;
    animationOutDelay: number;
}) {
    return isVisible
        ? {
              animation: animationIn,
              duration: animationInDuration,
              delay: animationInDelay,
          }
        : {
              animation: animationOut,
              duration: animationOutDuration,
              delay: animationOutDelay,
          };
}

/**
 * Ported from https://www.npmjs.com/package/react-animated-css due to high bundle size.
 */
const AnimatedCss: FC<
    PropsWithChildren<{
        animateOnMount?: boolean;
        isVisible?: boolean;
        animationIn?: string;
        animationOut?: string;
        animationInDelay?: number;
        animationOutDelay?: number;
        animationInDuration?: number;
        animationOutDuration?: number;
        className?: string;
        style?: CSSProperties;
    }>
> = ({
    animateOnMount = true,
    isVisible = true,
    animationIn = "fadeIn",
    animationOut = "fadeOut",
    animationInDelay = 0,
    animationOutDelay = 0,
    animationInDuration = 1000,
    animationOutDuration = 1000,
    className = "",
    style = {},
    children,
}) => {
    const [{ animation, duration, delay }, setAnimationRelatedState] = useState<
        ReturnType<typeof getAnimationRelatedState>
    >(
        animateOnMount
            ? getAnimationRelatedState({
                  isVisible,
                  animationIn,
                  animationOut,
                  animationInDelay,
                  animationOutDelay,
                  animationInDuration,
                  animationOutDuration,
              })
            : {
                  animation: "",
                  delay: undefined,
                  duration: 0,
              },
    );

    useEffect(() => {
        setAnimationRelatedState(
            getAnimationRelatedState({
                isVisible,
                animationIn,
                animationOut,
                animationInDelay,
                animationOutDelay,
                animationInDuration,
                animationOutDuration,
            }),
        );
    }, [
        isVisible,
        animationIn,
        animationOut,
        animationInDelay,
        animationOutDelay,
        animationInDuration,
        animationOutDuration,
    ]);

    const isNoAnimation = !animation || animation === "none";
    const classes = isNoAnimation ? className : `animate__animated animate__${animation} ${className}`;
    const backwardStyle = isNoAnimation
        ? {
              opacity: isVisible ? 1 : 0,
              ...(duration > 0 || delay > 0 ? { transition: `opacity ${duration || delay}ms` } : {}),
          }
        : {};

    return (
        <div
            className={classes}
            style={{
                animationDelay: `${delay}ms`,
                animationDuration: `${duration}ms`,
                pointerEvents: isVisible ? "all" : "none",
                ...style,
                ...backwardStyle,
            }}
        >
            {children}
        </div>
    );
};

export { AnimatedCss };
