import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { Manager, Popper, Reference } from 'react-popper';
import classNames from 'classnames';
import { isMobile as isMobileUtil } from 'Utilities/mobile';
import { kinds, tooltipPositions } from 'Helpers/Props';
import Portal from 'Components/Portal';
import dimensions from 'Styles/Variables/dimensions';
import styles from './Tooltip.css';

let maxWidth = null;

function getMaxWidth() {
  const windowWidth = window.innerWidth;

  if (windowWidth >= parseInt(dimensions.breakpointLarge)) {
    maxWidth = 800;
  } else if (windowWidth >= parseInt(dimensions.breakpointMedium)) {
    maxWidth = 650;
  } else if (windowWidth >= parseInt(dimensions.breakpointSmall)) {
    maxWidth = 500;
  } else {
    maxWidth = 450;
  }

  return maxWidth;
}

class Tooltip extends Component {

  //
  // Lifecycle

  constructor(props, context) {
    super(props, context);

    this._scheduleUpdate = null;
    this._closeTimeout = null;
    this._maxWidth = maxWidth || getMaxWidth();

    this.state = {
      isOpen: false
    };
  }

  componentDidUpdate() {
    if (this._scheduleUpdate && this.state.isOpen) {
      this._scheduleUpdate();
    }
  }

  componentWillUnmount() {
    if (this._closeTimeout) {
      this._closeTimeout = clearTimeout(this._closeTimeout);
    }
  }

  //
  // Control

  computeMaxSize = (data) => {
    const {
      top,
      right,
      bottom,
      left
    } = data.offsets.reference;

    const windowWidth = window.innerWidth;
    const windowHeight = window.innerHeight;

    if ((/^top/).test(data.placement)) {
      data.styles.maxHeight = top - 20;
    } else if ((/^bottom/).test(data.placement)) {
      data.styles.maxHeight = windowHeight - bottom - 20;
    } else if ((/^right/).test(data.placement)) {
      data.styles.maxWidth = Math.min(this._maxWidth, windowWidth - right - 20);
      data.styles.maxHeight = top - 20;
    } else {
      data.styles.maxWidth = Math.min(this._maxWidth, left - 20);
      data.styles.maxHeight = top - 20;
    }

    return data;
  }

  //
  // Listeners

  onMeasure = ({ width }) => {
    this.setState({ width });
  }

  onClick = () => {
    if (isMobileUtil()) {
      this.setState({ isOpen: !this.state.isOpen });
    }
  }

  onMouseEnter = () => {
    if (this._closeTimeout) {
      this._closeTimeout = clearTimeout(this._closeTimeout);
    }

    this.setState({ isOpen: true });
  }

  onMouseLeave = () => {
    this._closeTimeout = setTimeout(() => {
      this.setState({ isOpen: false });
    }, 100);
  }

  //
  // Render

  render() {
    const {
      className,
      bodyClassName,
      anchor,
      tooltip,
      kind,
      position,
      canFlip
    } = this.props;

    return (
      <Manager>
        <Reference>
          {({ ref }) => (
            <span
              ref={ref}
              className={className}
              onClick={this.onClick}
              onMouseEnter={this.onMouseEnter}
              onMouseLeave={this.onMouseLeave}
            >
              {anchor}
            </span>
          )}
        </Reference>

        <Portal>
          <Popper
            placement={position}
            // Disable events to improve performance when many tooltips
            // are shown (Quality Definitions for example).
            eventsEnabled={false}
            modifiers={{
              computeMaxHeight: {
                order: 851,
                enabled: true,
                fn: this.computeMaxSize
              },
              preventOverflow: {
                // Fixes positioning for tooltips in the queue
                // and likely others.
                escapeWithReference: false
              },
              flip: {
                enabled: canFlip
              }
            }}
          >
            {({ ref, style, placement, arrowProps, scheduleUpdate }) => {
              this._scheduleUpdate = scheduleUpdate;

              const popperPlacement = placement ? placement.split('-')[0] : position;
              const vertical = popperPlacement === 'top' || popperPlacement === 'bottom';

              return (
                <div
                  ref={ref}
                  className={classNames(
                    styles.tooltipContainer,
                    vertical ? styles.verticalContainer : styles.horizontalContainer
                  )}
                  style={style}
                  onMouseEnter={this.onMouseEnter}
                  onMouseLeave={this.onMouseLeave}
                >
                  <div
                    className={this.state.isOpen ? classNames(
                      styles.arrow,
                      styles[kind],
                      styles[popperPlacement]
                    ) : styles.arrowDisabled}
                    ref={arrowProps.ref}
                    style={arrowProps.style}
                  />
                  {
                    this.state.isOpen ?
                      <div
                        className={classNames(
                          styles.tooltip,
                          styles[kind]
                        )}
                      >
                        <div
                          className={bodyClassName}
                        >
                          {tooltip}
                        </div>
                      </div> :
                      null
                  }
                </div>
              );
            }}
          </Popper>
        </Portal>
      </Manager>
    );
  }
}

Tooltip.propTypes = {
  className: PropTypes.string,
  bodyClassName: PropTypes.string.isRequired,
  anchor: PropTypes.node.isRequired,
  tooltip: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
  kind: PropTypes.oneOf([kinds.DEFAULT, kinds.INVERSE]),
  position: PropTypes.oneOf(tooltipPositions.all),
  canFlip: PropTypes.bool.isRequired
};

Tooltip.defaultProps = {
  bodyClassName: styles.body,
  kind: kinds.DEFAULT,
  position: tooltipPositions.TOP,
  canFlip: false
};

export default Tooltip;