import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useId } from '@reach/auto-id';
import { ClickAwayListener } from '../../helpers/clickAwayListener/ClickAwayListener';
import { modifierCSS } from '../../../helpers';

const propTypes = {
  /**
   * Children to set inside the Modal.
   *
   * @see ModalHeader
   * @see ModalBody
   * @see ModalFooter
   */
  children: PropTypes.node,
  defaultClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
  /**
   * Class name for the Modal.
   */
  className: PropTypes.string,
  /**
   * The attributes to give to the content of the modal.
   */
  contentAttributes: PropTypes.object,
  /**
   * The tag of the content of the modal.
   */
  contentTag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
  /**
   * The default class name of the content of the modal.
   */
  contentDefaultClassName: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.arrayOf(PropTypes.string),
  ]),
  /**
   * Disable the outside click. Note that by default the outside click is enable and the overlay
   * is considered as outside.
   */
  isClickOutsideDisabled: PropTypes.bool,
  /**
   * Disable the dismiss of the modal using Escape key.
   */
  isEscapeKeyDisabled: PropTypes.bool,
  /**
   * Boolean to set the modal as fullscreen variant.
   */
  isFullscreen: PropTypes.bool,
  /**
   * Boolean to open or close the
   */
  isOpen: PropTypes.bool,
  isPortalDisabled: PropTypes.bool,
  /**
   * Callback function trigerred on close event.
   */
  onClose: PropTypes.func,
  portalContainer: PropTypes.instanceOf(Element),
  /**
   * The size of the Modal
   */
  size: PropTypes.oneOf(['', 'xxs', 'xs', 'sm', 'lg', 'xl', 'xxl']),
  /**
   * The custom tag for the root of the Modal
   */
  tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
};

const defaultProps = {
  children: '',
  className: '',
  contentAttributes: {},
  contentTag: 'div',
  contentDefaultClassName: 'sui-o-modal__content',
  defaultClassName: 'sui-o-modal',
  isClickOutsideDisabled: false,
  isEscapeKeyDisabled: false,
  isFullscreen: false,
  isOpen: false,
  isPortalDisabled: false,
  portalContainer: null,
  onClose: () => {},
  size: '',
  tag: 'div',
};

export const ModalContext = React.createContext({});
export const useModalContext = () => React.useContext(ModalContext);

export const Modal = ({
  children,
  className,
  contentAttributes,
  contentTag: ContentTag,
  contentDefaultClassName,
  defaultClassName,
  isClickOutsideDisabled,
  isEscapeKeyDisabled,
  isFullscreen,
  isOpen,
  onClose,
  size,
  isPortalDisabled,
  portalContainer,
  tag: Tag,
  ...attributes
}) => {
  const classes = classnames(
    defaultClassName,
    className,
    isOpen ? 'as--open' : '',
    isFullscreen ? 'as--fullscreen' : '',
    modifierCSS(size, 'size'),
  );

  const contentClasses = classnames(
    contentDefaultClassName,
  );

  const handleClose = (event) => {
    event.stopPropagation();

    if (onClose) {
      onClose();
    }
  };

  // Escape Key Listener
  useEffect(() => {
    /**
   * Function to handle the Escape key down.
   * @param {KeyboardEvent} event
   */
    const handleKeyDown = (event) => {
      if (event.key !== 'Escape' && event.keyCode !== 27) {
        return;
      }

      // Swallow the event, in case someone is listening for the escape key on the body.
      event.stopPropagation();

      if (!isEscapeKeyDisabled) {
        handleClose();
      }
    };

    if (!isEscapeKeyDisabled && isOpen) {
      window.addEventListener('keydown', handleKeyDown);
    }

    return () => {
      window.removeEventListener('keydown', handleKeyDown);
    };
  }, [isOpen]);

  const id = useId();

  const mountModal = () => {
    const portalTarget = portalContainer || document.body;
    const modal = (
      <Tag className={classes} {...attributes}>
        <ClickAwayListener
          onClickOutside={handleClose}
          isClickOutsideDisabled={isClickOutsideDisabled}
        >
          <ContentTag role="dialog" aria-labelledby={`dialog:${id}:label`} aria-modal="true" id={`dialog:${id}`} className={contentClasses} {...contentAttributes}>
            {children}
          </ContentTag>
        </ClickAwayListener>
      </Tag>
    );

    if (isPortalDisabled) {
      return modal;
    }

    return ReactDOM.createPortal(modal, portalTarget);
  };

  return (
    <ModalContext.Provider
      value={{
        handleClose,
        id,
        isOpen
      }}
    >
      {mountModal()}
    </ModalContext.Provider>
  );
};

Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;
