// @flow
import React, { useEffect, useCallback } from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
import { UnstyledButton } from '../../behaviors';
import { Resets } from '../../../themes/patchwork-theme-provider';
import { Box } from '../../../primitives';
import { Remove } from '../../icon';
import { useFocusTrap } from './use-focus-trap';

import { modalPropTypes, type ModalProps } from './modal.proptypes';
import {
  getBackgroundColor,
  getContentBackground,
  getBorderColor,
  getContentBoxShadowColor,
  getModalWidth,
  getModalHeaderPadding,
} from './modal.css-rules';

export const ModalBackground = styled(Box)`
  display: block;
  position: fixed;
  z-index: 1;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: ${getBackgroundColor};
`;

export const ModalHeader = styled(Box)`
  height: ${getModalHeaderPadding};
  position: relative;
`;

export const ModalContent = styled(Box)`
  max-width: ${getModalWidth};
  background-color: ${getContentBackground};
  box-shadow: 0 5px 15px 0 ${getContentBoxShadowColor};
  border: solid 1px ${getBorderColor};
  border-radius: 15px;
  z-index: 2;
  margin: 15vh auto;
  width: 80%;

  &:focus {
    border: 0;
    outline: none;
  }
`;

export const ModalRemove = styled(UnstyledButton)`
  padding-top: 20px;
  padding-right: 20px;
  position: absolute;
  right: 0;
`;

function getBody() {
  const documentElement =
    typeof document !== 'undefined' ? document : undefined;
  return documentElement ? documentElement.body : null;
}

function lockBodyOverflow() {
  const body = getBody();

  if (body !== null && body.dataset.initialOverflow === undefined) {
    body.dataset.initialOverflow = body.style.overflow;
    body.style.overflow = 'hidden';
  }
}

function unlockBodyOverflow() {
  const body = getBody();

  if (body !== null && body.dataset.initialOverflow !== undefined) {
    body.style.overflow = body.dataset.initialOverflow;
    delete body.dataset.initialOverflow;
  }
}

export const Modal = ({
  show,
  persistent = false,
  onClose,
  size = 'lg',
  children,
  className = '',
  spacing,
  hideHeader = false,
  refToRestoreFocus,
}: ModalProps) => {
  const modalRef = useFocusTrap({ show, refToRestoreFocus });

  const body = getBody();

  useEffect(() => {
    if (body) {
      return () => {
        unlockBodyOverflow();
      };
    }
  }, [body]);

  const handleBackgroundClick = useCallback(
    event => {
      event.preventDefault();
      if (!persistent) {
        onClose();
      }
    },
    [onClose, persistent],
  );

  const handleContentClick = useCallback(event => {
    event.stopPropagation();
  }, []);

  const handleCloseClick = useCallback(
    event => {
      event.stopPropagation();
      onClose();
    },
    [onClose],
  );

  const handleKeyPress = useCallback(
    event => {
      if (show && !persistent && event.code === 'Escape') {
        onClose();
      }
    },
    [onClose, show, persistent],
  );

  useEffect(() => {
    if (show && !persistent) {
      window.addEventListener('keydown', handleKeyPress);
    }

    return () => {
      window.removeEventListener('keydown', handleKeyPress);
    };
  }, [handleKeyPress, show, persistent]);

  if (body == null) return null;

  if (show) {
    lockBodyOverflow();
  } else {
    unlockBodyOverflow();
  }

  return (
    show &&
    ReactDOM.createPortal(
      <Resets face="sans">
        <ModalBackground onClick={handleBackgroundClick}>
          <ModalContent
            onClick={handleContentClick}
            size={size}
            className={className}
            ref={modalRef}
            role="dialog"
            aria-modal="true"
          >
            {!hideHeader && (
              <ModalHeader stacked="row" size={size}>
                {!persistent && (
                  <ModalRemove insetFocus onClick={handleCloseClick}>
                    <Remove size={12} color="greyscale-dark" />
                  </ModalRemove>
                )}
              </ModalHeader>
            )}

            <Box spacing={spacing || ['ph6', 'pb6']}>{children}</Box>
          </ModalContent>
        </ModalBackground>
      </Resets>,
      body,
    )
  );
};

Modal.propTypes = modalPropTypes;

export { useModal } from './use-modal';
