import * as React from 'react';
import cx from 'classnames';
import { HoverProvider } from './hoverContext';
import { bool, func, number, oneOf, oneOfType, node } from 'prop-types';
import useControlProp from '../../hooks/useControlProp';
import { MoveFocusInside } from 'react-focus-lock';
import Positioner, { Position } from '../Positioner';
import useOnOutsideClick from '../../hooks/useOnOutsideClick';
import useOnUpdate from '../../hooks/useOnUpdate';
import styles from './Popover.module.scss';

const Popover = ({
  target,
  children,
  shown: shownProp,
  onToggle,
  triggerOn,
  position,
  focusRadius,
  contentClassName,
}) => {
  const [shown, setShown] = useControlProp(shownProp, onToggle, false);
  const contentRef = React.useRef();
  const buttonRef = React.useRef();
  const hoverRef = React.useRef();

  useOnOutsideClick(
    [contentRef, buttonRef, hoverRef],
    () => setShown(false),
    shown && triggerOn === 'click',
  );

  function handleKeyDown(event) {
    if (event.key === 'Escape' && shown) {
      event.stopPropagation();
      setShown(false);
    } else if (event.key === ' ' || event.key === 'Enter') {
      event.stopPropagation();
    }
  }

  useOnUpdate(() => {
    if (!shown) {
      buttonRef.current.focus();
    }
  }, [shown]);

  return (
    <div onClick={(e) => e.stopPropagation()} onKeyDown={handleKeyDown}>
      <Positioner
        targetRef={buttonRef}
        contentRef={contentRef}
        shown={shown}
        position={position}
        renderContent={({ ref, style }) => (
          <div ref={ref} style={style} className={contentClassName}>
            {shown && (
              <MoveFocusInside>
                <HoverProvider value={hoverRef}>
                  {typeof children === 'function' ? children(shown) : children}
                </HoverProvider>
              </MoveFocusInside>
            )}
          </div>
        )}
      >
        {({ ref }) => (
          <button
            type="button"
            ref={ref}
            className={cx(styles.button, shown && styles.active)}
            onClick={() => setShown(!shown)}
            style={{ borderRadius: focusRadius }}
          >
            {typeof target === 'function' ? target(shown) : target}
          </button>
        )}
      </Positioner>
    </div>
  );
};

Popover.propTypes = {
  shown: bool,
  onToggle: func,
  position: oneOf(Object.values(Position)),
  children: oneOfType([node, func]).isRequired,
  target: oneOfType([node, func]).isRequired,
  triggerOn: oneOf(['click', 'hover']),
  focusRadius: number,
  offset: number,
};

Popover.defaultProps = {
  shown: undefined,
  onToggle: () => {},
  position: Position.BOTTOM_LEFT,
  target: undefined,
  triggerOn: 'click',
  focusRadius: 6,
  offset: 6,
};

export default Popover;
