/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';

// Style
let classes = {};
/* istanbul ignore next */
if (typeof window !== 'undefined' || typeof _COMPILED_ === 'undefined') classes = require('./style.module.scss');

const classy = (...classlist) => classlist.filter(e => e).join(' ');

class Modal extends React.Component {
  static propTypes = {
    children: PropTypes.any,
    className: PropTypes.string,
    id: PropTypes.string,
    noDismiss: PropTypes.bool,
    onClose: PropTypes.func,
    onOpen: PropTypes.func,
    /** The modal's initial state.
     */
    open: PropTypes.bool,
    scrollable: PropTypes.bool,
    size: PropTypes.oneOf(['sm', 'md', 'lg']),
    /** DOM element to inject the modal in to.
     */
    target: PropTypes.string,
    verticalCenter: PropTypes.bool,
  };

  static defaultProps = {
    open: false,
    target: 'body',
  };

  constructor(props) {
    super(props);

    this.root = typeof window !== 'undefined' ? document.querySelector(props.target) : null;
    this.el = React.createRef();

    this.state = {
      open: !!props.open,
    };
  }

  componentDidMount() {
    if (this.root) {
      if (!this.props.noDismiss) {
        this.root.addEventListener('click', this.handleClickOut);
        if (this.state.open) this.root.classList.add(classes.Modal_open);
      }
      if (this.props.noDismiss) this.root.classList.add('ModalWrapper_noDismiss');
      if (this.props.verticalCenter) this.root.classList.add('ModalWrapper_verticalCenter');
    }
  }

  componentWillUnmount() {
    if (this.root && !this.props.noDismiss) {
      this.root.removeEventListener('click', this.handleClickOut);
    }
  }

  toggle(toState) {
    // eslint-disable-next-line no-param-reassign
    toState = typeof toState === 'boolean' ? toState : !this.state.open;

    this.setState({ open: toState });

    setTimeout(() => {
      const { onClose, onOpen } = this.props;
      const { current: el } = this.el;
      if (toState) {
        this.root.classList.add(classes.Modal_open);
        el.focus(); // focus modal
        if (typeof onOpen === 'function') onOpen();
      } else {
        this.root.classList.remove(classes.Modal_open);
        if (typeof onClose === 'function') onClose();
      }
    }, 0);
  }

  handleClickOut = ({ target }) => {
    if (target === this.root) this.toggle(false);
  };

  render() {
    const { open } = this.state;
    const { className, children, noDismiss, scrollable, size, verticalCenter, ...attrs } = this.props;
    const modalClasses = [
      'Box',
      'Box_modal',
      'Modal',
      open && 'Modal_open',
      scrollable && 'Modal_scrollable',
      size && `Modal_${size}`,
      verticalCenter && 'Modal_verticalCenter',
      className,
    ].map(cn => (cn in classes ? classes[cn] : cn));

    return this.root
      ? ReactDOM.createPortal(
          <section
            ref={this.el}
            aria-hidden={!open}
            aria-modal={true}
            className={classy(...modalClasses)}
            onKeyDown={({ key }) => {
              if (!noDismiss && key === 'Escape') this.toggle(false);
            }}
            role="dialog"
            tabIndex={0}
            {...attrs}
          >
            {this.props.children}
          </section>,
          this.root
        )
      : null;
  }
}

export default Modal;
