import React from 'react';
import { string, elementType, bool, node, oneOf } from 'prop-types';
import css from '@styled-system/css';
import styled from '@emotion/styled';
import { isTargetWithin } from '../../hooks/useOnOutsideClick';
import { focusVisibleRing } from '../../utils/styled';
import Positioner, { Position } from '../Positioner';
import useControlProp from '../../hooks/useControlProp';
import Box from '../Layout/Box/index.jsx';

const StyledWrapper = styled('span')(
  {
    width: 'fit-content',
  },
  ({ underline }) => ({
    borderBottom: underline && '1px dotted currentColor',
  }),
);

const StyledTooltip = styled('span')(
  css({
    borderRadius: '4px',
    lineHeight: 1.4,
    font: 'xxsmall',
    color: 'tooltipForeground',
    bg: 'tooltipBackground',
    fontSize: 'standard',
    maxWidth: '353px',
    maxHeight: '266px',
    width: 'max-content',
    height: 'max-content',
    overflowY: 'auto',
  }),
  focusVisibleRing(),
);

const Tooltip = ({
  children,
  as: Comp = 'span',
  content,
  shown: shownProp,
  onToggle,
  underline = false,
  position = Position.LEFT,
  delayShow = 200,
  delayHide = 100,
  contentClassName,
  wrapperClassName,
  disable,
  afterHide = () => {},
  className,
  ...rest
}) => {
  const [shown, setShown] = useControlProp(shownProp, onToggle, false);
  const tooltipContentRef = React.useRef();
  const childRef = React.useRef();
  let timeout;

  if (shown && childRef.current && childRef.current.matches(':hover') === false) {
    setShown(false);
  }

  function handleMouseEnter() {
    if (disable) return;
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      setShown(true);
    }, delayShow);
  }

  function handleMouseLeave(e) {
    if (disable) return;
    clearTimeout(timeout);
    if (!isTargetWithin(e.target, childRef.current)) return;
    timeout = setTimeout(() => {
      setShown(false);
      afterHide(e);
    }, delayHide);
  }

  return (
    <Comp
      className={wrapperClassName}
      {...rest}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
      <Positioner
        targetRef={childRef}
        contentRef={tooltipContentRef}
        shown={shown}
        position={position}
        renderContent={({ ref, style }) => (
          <React.Fragment>
            {!disable && (
              <StyledTooltip
                className={contentClassName}
                style={style}
                ref={ref}
                onMouseLeave={() => setShown(false)}
              >
                <Box p="xsmall">{content}</Box>
              </StyledTooltip>
            )}
          </React.Fragment>
        )}
      >
        {({ ref }) => (
          <StyledWrapper ref={ref} as={Comp} underline={underline} className={className}>
            {children}
          </StyledWrapper>
        )}
      </Positioner>
    </Comp>
  );
};

export default Tooltip;

Tooltip.propTypes = {
  as: elementType,
  underline: bool,
  children: node.isRequired,
  content: node,
  position: oneOf(Object.values(Position)),
  className: string,
  wrapperClassName: string,
  contentClassName: string,
};
