import cls from 'classnames';
import {CSSTransition} from 'react-transition-group';
import {TransitionChildren} from 'react-transition-group/Transition';
import './transition.styl';

export type TransitionType =
    'fade-in'
    | 'fade-in-up'
    | 'fade-in-up-slight'
    | 'fade-in-up-self'
    | 'fade-in-down'
    | 'fade-in-down-slight'
    | 'fade-in-down-self'
    | 'fade-in-left'
    | 'fade-in-left-slight'
    | 'fade-in-left-self'
    | 'fade-in-right'
    | 'fade-in-right-slight'
    | 'fade-in-right-self'
    | 'zoom-in-inward'
    | 'zoom-in-outward';

interface TransitionProps {
    onEnter?(elm: HTMLElement): void;
    onEntering?(elm: HTMLElement): void;
    onEntered?(elm: HTMLElement): void;
    onExit?(elm: HTMLElement): void;
    onExiting?(elm: HTMLElement): void;
    onExited?(elm: HTMLElement): void;
    disabled?: boolean;
    enter?: boolean;
    exit?: boolean;
    enterDuration?: number;
    enterDelay?: number;
    exitDuration?: number;
    exitDelay?: number;
    in?: boolean;
    type?: TransitionType;
    className?: string;
    children?: TransitionChildren;
}

export const Transition = (props: TransitionProps) => {
    const {
        onEnter = () => {},
        onEntering = () => {},
        onEntered = () => {},
        onExit = () => {},
        onExiting = () => {},
        onExited = () => {},
        enterDuration = 500,
        enterDelay = 0,
        disabled = false,
        exitDuration = 500,
        exitDelay = 0,
        in: transitionIn = true,
        enter = true,
        exit = false,
        type = 'fade-in',
        className,
        children
    } = props;

    const enterTotalDuration = enterDuration + enterDelay;
    const exitTotalDuration = exitDuration + exitDelay;
    const transitionApply = 'react-transition-apply'; // add css specificity
    const transitionApply1 = `${transitionApply}1`; // add css specificity
    const transitionApply2 = `${transitionApply}2`; // add css sepcificity

    /*
     ****************************************
     * Event Handlers
     ****************************************
     */
    function enterHandler(elm: HTMLElement) {
        if (enterDelay) elm.style.transitionDelay = `${enterDelay}ms`;
        elm.classList.add(transitionApply);
        elm.classList.add(transitionApply1);
        elm.classList.add(transitionApply2);
        onEnter(elm);
    }

    function enteringHandler(elm: HTMLElement) {
        onEntering(elm);
    }

    function enteredHandler(elm: HTMLElement) {
        if (enterDelay) elm.style.transitionDelay = '';
        elm.classList.remove(transitionApply);
        elm.classList.remove(transitionApply1);
        elm.classList.remove(transitionApply2);
        onEntered(elm);
    }

    function exitHandler(elm: HTMLElement) {
        if (exitDelay) elm.style.transitionDelay = `${exitDelay}ms`;
        elm.classList.add(transitionApply);
        elm.classList.add(transitionApply1);
        elm.classList.add(transitionApply2);
        onExit(elm);
    }

    function exitingHandler(elm: HTMLElement) {
        onExiting(elm);
    }

    function exitedHandler(elm: HTMLElement) {
        if (exitDelay) elm.style.transitionDelay = '';
        elm.classList.remove(transitionApply);
        elm.classList.remove(transitionApply1);
        elm.classList.remove(transitionApply2);
        onExited(elm);
    }

    return (
        <>
            {transitionIn && disabled && children}
            {!disabled && (
                <CSSTransition
                    classNames={cls(type, className)}
                    onEnter={enterHandler}
                    onEntering={enteringHandler}
                    onEntered={enteredHandler}
                    onExit={exitHandler}
                    onExiting={exitingHandler}
                    onExited={exitedHandler}
                    timeout={{enter: enterTotalDuration, exit: exitTotalDuration}}
                    enter={enter && !!enterDuration}
                    exit={exit && !!exitDuration}
                    in={transitionIn}
                    mountOnEnter
                    unmountOnExit
                    appear
                >
                    {children}
                </CSSTransition>
            )}
        </>
    );
};
