/* eslint-disable no-undef */
import React, {
  useEffect, useMemo, useState, useRef,
} from 'react';
import PropTypes from 'prop-types';
import { Manager, Reference } from 'react-popper';
import FocusTrap from 'focus-trap-react';
import classnames from 'classnames';
import { ClickAwayListener } from '../../helpers/clickAwayListener/ClickAwayListener';
import { usePopper } from '../../../helpers';
import { Button } from '../../atoms/button/Button';

export const stopPropagationForConfirmation = (event, action) => {
  event.preventDefault();
  event.stopPropagation();
  action();
};

const propTypes = {
  /**
  * The label for the cancel button.
  */
  cancelLabel: PropTypes.node,
  /**
   * The color of the cancel button
   */
  cancelColor: PropTypes.oneOf(['', 'primary', 'secondary', 'warning', 'danger']),
  /**
   * The children that will be rendered by Confirm. The element onClick event
   * will be intercepted by Confirm.
   */
  children: PropTypes.element.isRequired,
  className: PropTypes.string,
  /**
   * The label for the confirmation button.
   */
  confirmLabel: PropTypes.node,
  /**
   * The color of the confirmation button
   */
  confirmColor: PropTypes.oneOf(['', 'primary', 'secondary', 'warning', 'danger']),
  /**
   * The content of the Confirm element.
   */
  content: PropTypes.node,
  defaultClassName: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.arrayOf(PropTypes.string),
  ]),
  /**
   * Disable the escape key down listener.
   */
  isEscapeKeyDisabled: PropTypes.bool,
  /**
   * Enable or disable the click away behavior. By default, the confirm will
   * close on click away.
   */
  isClickOutsideDisabled: PropTypes.bool,
  /**
   * Render the Confirm in the current DOM hierarchy.
   */
  isPortalDisabled: PropTypes.bool,
  /**
   * Render the Confirm element in one of the available placement.
   */
  placement: PropTypes.oneOf(['bottom', 'left', 'top', 'right']),
  /**
   * The target where the confirm node should render.
   */
  portalTarget: PropTypes.instanceOf(Element),
  /**
   * The component used for the root node.
   * Either a string to use a DOM element or a component.
   */
  tag: PropTypes.elementType,
  /**
   * The title of the Confirm.
   */
  title: PropTypes.node,
};

const defaultProps = {
  cancelLabel: 'Cancel',
  cancelColor: '',
  className: '',
  confirmLabel: 'Confirm',
  confirmColor: 'primary',
  content: '',
  defaultClassName: 'sui-o-popup as--open',
  isEscapeKeyDisabled: false,
  isClickOutsideDisabled: false,
  isPortalDisabled: false,
  placement: 'top',
  portalTarget: document.body,
  tag: 'div',
  title: 'Are you sure?',
};

export const Confirm = ({
  cancelLabel,
  cancelColor,
  children,
  className,
  confirmLabel,
  confirmColor,
  content,
  defaultClassName,
  isEscapeKeyDisabled,
  isClickOutsideDisabled,
  isPortalDisabled: isDisabled,
  placement,
  portalTarget: target,
  tag: Tag,
  title,
  ...attributes
}) => {
  if (!children || !children.props) {
    return '';
  }

  const [isOpen, setIsOpen] = useState(false);
  const { mountPopperNode } = usePopper(placement, { isDisabled, target });
  const callbackRef = useRef(null);

  const classes = classnames(
    defaultClassName,
    className,
  );

  const handleClose = () => {
    setIsOpen(false);
  };

  const handleShow = (callbackFunction) => () => {
    callbackRef.current = callbackFunction;
    setIsOpen(true);
  };

  const handleConfirm = () => {
    if (callbackRef.current) {
      callbackRef.current();
    }
    handleClose();
  };

  /**
   * 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();
    }
  };

  // Escape Key Listener
  useEffect(() => {
    if (!isEscapeKeyDisabled) {
      window.addEventListener('keydown', handleKeyDown);
    }

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

  const uniqueId = useMemo(() => Math.random().toString(36).substr(2, 9), []);

  return (
    <Manager>
      <Reference>
        {({ ref }) => (
          React.cloneElement(children, {
            ref,
            onClick: (e) => stopPropagationForConfirmation(e, handleShow(children.props.onClick)),
          })
        )}
      </Reference>

      {isOpen && (mountPopperNode(
        ({ ref, style }) => (
          <ClickAwayListener
            isClickOutsideDisabled={isClickOutsideDisabled}
            onClickOutside={() => setIsOpen(false)}
          >
            <FocusTrap>
              <Tag
                className={classes}
                {...attributes}
                ref={ref}
                style={style}
                role="alertdialog"
                aria-labelledby={`sui-confirm-title-${uniqueId}`}
                aria-describedby={`sui-confirm-content-${uniqueId}`}
              >
                {!!title && <div id={`sui-confirm-title-${uniqueId}`} className="sui-o-popup__title">{title}</div>}
                {!!content && <div id={`sui-confirm-content-${uniqueId}`} className="sui-o-popup__content">{content}</div>}
                <div className="sui-o-popup__buttons">
                  <Button type="button" onClick={handleClose} color={cancelColor}>
                    {cancelLabel}
                  </Button>
                  <Button
                    type="submit"
                    color={confirmColor}
                    onClick={handleConfirm}
                  >
                    {confirmLabel}
                  </Button>
                </div>
              </Tag>
            </FocusTrap>
          </ClickAwayListener>
        )))}
    </Manager>
  );
};

Confirm.propTypes = propTypes;
Confirm.defaultProps = defaultProps;
