import { ReactNode, useCallback, useRef } from 'react';
import { CSSTransition } from 'react-transition-group';

import { assertEmptyObject } from '../../../../utils/assertEmptyObject';

import { StyledToast, animationDuration } from './styled';

/** Props for {@link ToastItem} */
export interface ToastItemProps {
  /** Is item should be visible */
  visible: boolean;
  /** Emits when item hidden animation will be finished */
  onHidden: () => void;
  children: ReactNode;
}

/** Component that implement animation for showing and hiding Toast item */
export function ToastItem(props: ToastItemProps) {
  const { visible, onHidden, children, ...rest } = props;
  assertEmptyObject(rest);

  const elementOriginalHeight = useRef<number>(0);
  const transitionNodeRef = useRef<HTMLDivElement | null>(null);

  const setTransitionNodeRef = useCallback((element: HTMLDivElement | null) => {
    if (element) {
      elementOriginalHeight.current = element.offsetHeight;
    } else {
      elementOriginalHeight.current = 0;
    }

    transitionNodeRef.current = element;
  }, []);

  return (
    <CSSTransition
      appear
      in={visible}
      nodeRef={transitionNodeRef}
      onExited={onHidden}
      onEnter={() => {
        /* istanbul ignore next */
        if (!transitionNodeRef.current) {
          return;
        }

        transitionNodeRef.current.style.height = '0';
      }}
      onEntered={() => {
        /* istanbul ignore next */
        if (!transitionNodeRef.current) {
          return;
        }

        transitionNodeRef.current.style.removeProperty('height');
      }}
      onEntering={() => {
        /* istanbul ignore next */
        if (!transitionNodeRef.current) {
          return;
        }

        transitionNodeRef.current.style.height = `${elementOriginalHeight.current}px`;
      }}
      onExit={() => {
        /* istanbul ignore next */
        if (!transitionNodeRef.current) {
          return;
        }

        transitionNodeRef.current.style.height = `${elementOriginalHeight.current}px`;
      }}
      onExiting={() => {
        setTimeout(() => {
          /* istanbul ignore next */
          if (!transitionNodeRef.current) {
            return;
          }

          transitionNodeRef.current.style.height = '0';
        }, animationDuration);
      }}
      timeout={{
        appear: animationDuration,
        enter: animationDuration,
        exit: animationDuration * 2,
      }}
    >
      <StyledToast ref={setTransitionNodeRef}>{children}</StyledToast>
    </CSSTransition>
  );
}
