/** @jsxImportSource @emotion/react */
import React, { FunctionComponent } from 'react';
import tw, { css, styled } from 'twin.macro';

/**
 * The amount of time in milliseconds for the
 * overlay in/out transition to complete.
 * This const is used to determine when to apply
 * the hidden state.
 */
const TRANSITION_DURATION = 300;

type TransitionState = 'entering' | 'entered' | 'exiting' | 'exited';

type OverlayProps = {
  isOpen: boolean;
  offsetTop?: number;
};

type OverlayWrapperProps = OverlayProps & {
  transitionState: TransitionState;
};

const OverlayWrapper = styled.div(
  ({ transitionState = 'exited', offsetTop = 0 }: OverlayWrapperProps) => [
    tw`fixed inset-x-0 z-50 bottom-0`,
    css`
      top: ${offsetTop}px;
    `,
    tw`transition-opacity duration-300`,

    transitionState === 'entered' && tw`opacity-100`,

    transitionState === 'entering' && tw`opacity-100 pointer-events-none`,

    transitionState === 'exiting' && tw`opacity-0 pointer-events-none`,

    transitionState === 'exited' && tw`opacity-0`,
  ],
);

export const Overlay: FunctionComponent</**
 * Omitting `as` property because it is incompatible
 * with `as` on styled.div
 */
Omit<React.HTMLProps<HTMLDivElement>, 'as'> & OverlayProps> = ({
  isOpen = false,
  offsetTop = 0,
  children,
  ...props
}) => {
  const [transitionState, setTransitionState] = React.useState<TransitionState>(
    isOpen ? 'entered' : 'exited',
  );
  React.useEffect(() => {
    let timeout = setTimeout(() => {
      setTransitionState((transitionState) => {
        if (transitionState === 'entering') {
          return 'entered';
        }
        if (transitionState === 'exiting') {
          return 'exited';
        }
        return transitionState;
      });
      timeout = null;
    }, TRANSITION_DURATION);
    return () => {
      if (!timeout) return;
      clearTimeout(timeout);
    };
  }, [isOpen, setTransitionState]);

  React.useEffect(() => {
    if (isOpen) {
      if (transitionState !== 'entered') setTransitionState('entering');
    } else {
      if (transitionState !== 'exited') setTransitionState('exiting');
    }
  }, [isOpen, setTransitionState]);
  return (
    <>
      {isOpen && (
        <OverlayWrapper
          offsetTop={offsetTop}
          isOpen={isOpen}
          transitionState={transitionState}
          {...props}
        >
          {children}
        </OverlayWrapper>
      )}
    </>
  );
};

export default Overlay;
