import { useCallback, useRef } from 'react';
import ReactDom from 'react-dom';

import type { CSSProperties, FC, ReactNode } from 'react';
import type { TransitionInOut2Api } from '../TransitionInOut2';

import useKeyboard from '~/src/hooks/useKeyboard';
import useTheme from '~/src/hooks/useTheme';
import Box from '../Box';
import Clickable from '../Clickable';
import Gradient from '../Gradient';
import CrossIcon from '../Icon/CrossIcon';
import Scroller2 from '../Scroller2';
import TransitionInOut2 from '../TransitionInOut2';

export type CloseModal = () => Promise<void>;

export interface ModalProps {
  onClose?: () => void;
  renderContent: (params: { close: () => void }) => ReactNode;
  renderHeader?: (params: { close: () => void }) => ReactNode;
  renderAfter?: (params: { close: () => void }) => ReactNode;
  testId?: string;
  zIndex?: number;
  style?: CSSProperties;
  withScroller?: boolean;
  withSlideIn?: boolean;
}

const Modal: FC<ModalProps> = ({
  onClose,
  renderContent,
  renderHeader = () => null,
  renderAfter = () => null,
  withSlideIn,
  withScroller,
  testId,
  zIndex = 1,
  style,
}) => {
  const rootElRef = useRef<HTMLDivElement>(null);
  const transitionApiRef = useRef<TransitionInOut2Api>(null);
  const theme = useTheme();

  const close: CloseModal = useCallback(async () => {
    await transitionApiRef.current?.setVisible(false, {
      duration: 150,
    });

    if (onClose) onClose();
  }, []);

  useKeyboard(
    {
      rootElRef,
      onEscape: close,
    },
    [renderContent]
  );

  const headerContent = renderHeader({ close }) || (
    <Clickable
      margin="0 0 0 auto"
      isInline
      onClick={close}
      padding="1.5rem"
      testId="closeModalButton"
    >
      <CrossIcon size="3.2rem" />
    </Clickable>
  );

  const Component = (
    <TransitionInOut2
      apiRef={transitionApiRef}
      nodeRef={rootElRef}
      style={{
        color: theme.textColor,
        zIndex,
      }}
      coverParent
      role="dialog"
      aria-modal="true"
      data-testid={testId}
      transitionOnMount
      duration={240}
      centerContent
      onClick={close}
      styleFrom={{
        opacity: 0,
        transform: withSlideIn ? 'translateY(7%)' : undefined,
      }}
    >
      <Box
        coverParent
        style={{
          background: theme.background,
          opacity: 0.8,
        }}
      />
      {(() => {
        if (withScroller) {
          return (
            <Scroller2
              // withOverflowGradients
              // HACK: ensure child scroller div grows to fill parent
              // this might want to be considered as part of the core styles.
              flexColumn
              // When there content height doesn't fill viewport leave space
              // above/below so clicks hit the backdrop and close the modal.
              fullHeight={false}
              style={{
                padding: '2rem',
                position: 'relative',
                overflow: 'hidden',
                maxHeight: '100%',
                ...style,
              }}
            >
              <div
                // stop clicks on content bubbling up to close handler
                onClick={(e) => e.stopPropagation()}
              >
                {renderContent({ close }) || null}
              </div>
            </Scroller2>
          );
        }

        return (
          <div
            style={{
              padding: '2rem',
              position: 'relative',
              overflow: 'hidden',
              maxHeight: '100%',
              ...style,
            }}
            // stop clicks on content bubbling up to close handler
            onClick={(e) => e.stopPropagation()}
          >
            {renderContent({ close }) || null}
          </div>
        );
      })()}
      <Gradient
        to="rgba(0,0,0,0.5)"
        angle={180}
        positionAbsolute
        top={0}
        left={0}
        right={0}
        flexRow
        alignCenter
      >
        {headerContent}
      </Gradient>
      {renderAfter({ close })}
    </TransitionInOut2>
  );

  return ReactDom.createPortal(Component, document.body);
};

export default Modal;
