import React, { Component } from "react";
import ReactDOM from "react-dom";
import styled, { css, keyframes } from "styled-components";
import Text from "@bufferapp/ui/Text";
import helpers from "./helperDictionary";

const TOOLTIP_MAXWIDTH = 550;
const TOOLTIP_OFFSET_X_LEFT = 20;
const TOOLTIP_OFFSET_X_RIGHT = 30;
const TOOLTIP_OFFSET_Y_TOP = 10;
const TOOLTIP_OFFSET_Y_BOTTOM = 20;
const TOOLTIP_CLIPPING_BUFFER = 10;

const animationTooltip = keyframes`
  0% { opacity: 0; }
  25% { opacity: 0; }
  100% { opacity: 1; }
`;

const Container = styled.div`
  display: inline-block;
`;

const Inner = styled.div`
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-start;

  &:hover {
    cursor: pointer;
  }
`;

const Tooltip = styled.div`
  display: none;
  opacity: 0;
  position: absolute;
  top: auto; /* calculated later */
  left: auto; /* calculated later */
  background: rgba(0, 0, 0, 0.8);
  padding: 6px;
  border-radius: 3px;
  z-index: 9999999;

  ${(props) =>
    props.show &&
    css`
      display: block;
      animation: ${animationTooltip} 300ms ease-out;
      opacity: 1;
    `}

  ${(props) =>
    props.wrap &&
    css`
      width: ${TOOLTIP_MAXWIDTH}px;

      span {
        white-space: normal;
      }
    `}

  span {
    font-weight: 400;
  }

  span b {
    font-weight: 800 !important;
  }
`;

function normalizeLabel(label) {
  // this is to ensure we strip any erronous capital letters from label
  return label.toLowerCase();
}

class Helper extends Component {
  constructor(props) {
    super(props);

    this.state = {
      show: false,
      left: 0,
      top: 0,
      wrap: false,
    };
  }

  showTooltip = (mouseEvent) => {
    const position = this.getTooltipPositions(mouseEvent);
    const left = position.x;
    const top = position.y;
    const wrap = this.shouldWrap();

    this.setState({ show: true, left, top, wrap });

    mouseEvent.stopPropagation();
  };

  hideTooltip = (mouseEvent) => {
    this.setState({ show: false, left: 0, top: 0, wrap: false });
  };

  getTooltipPositions = (mouseEvent) => {
    const bounder = mouseEvent.target.closest('[data-helper]').getBoundingClientRect(); // find the helper parent
  
    let offset = TOOLTIP_OFFSET_X_LEFT;
    const clipping = this.isTooltipClippingX();
    if (clipping) offset = this.getTooltipWidth() - TOOLTIP_OFFSET_X_RIGHT; // sensible offset

    const bounderX = bounder.left;
    const mouseX = mouseEvent.clientX;

    let positionX = mouseX - bounderX;
    if (positionX < 0) positionX = 0;
    positionX = positionX - offset;

    const isClippingAbove = this.isTooltipClippingY(mouseEvent);

    const bounderY = bounder.top;
    const mouseY = mouseEvent.clientY;

    let positionY = mouseY - bounderY;
    if (positionY < 0) positionY = 0;
    if (isClippingAbove) positionY = positionY + TOOLTIP_OFFSET_Y_BOTTOM;
    else positionY = positionY - this.getTooltipHeight() - TOOLTIP_OFFSET_Y_TOP;

    return {
      x: positionX,
      y: positionY,
    };
  }

  getTooltipWidth = () => {
    // get the tooltip width so we can later workout offset if aligning right
    const tooltipEl = ReactDOM.findDOMNode(this.tooltipEl);
    if (tooltipEl) {
      const tooltipRect = tooltipEl.getBoundingClientRect();
      return tooltipRect.width;
    }
    return false;
  };

  getTooltipHeight = () => {
    // get the tooltip height so we can later workout if tooltip is going too high
    const tooltipEl = ReactDOM.findDOMNode(this.tooltipEl);
    if (tooltipEl) {
      const tooltipRect = tooltipEl.getBoundingClientRect();
      return tooltipRect.height;
    }
    return false;
  };

  shouldWrap = () => {
    const tooltipEl = ReactDOM.findDOMNode(this.tooltipEl);
    const tooltipRect = tooltipEl.getBoundingClientRect();
    return (tooltipRect.width > TOOLTIP_MAXWIDTH ? true : false);
  }

  isTooltipClippingX = () => {
    // this is required to find boundary so to left or right align tooltip to avoid cutoff
    const tooltipEl = ReactDOM.findDOMNode(this.tooltipEl);
    const boundaryEl = tooltipEl.closest("[data-chart]"); //  // find the chart parent
    if (boundaryEl) {
      const iconEl = ReactDOM.findDOMNode(this.iconEl);
      const iconRect = iconEl.getBoundingClientRect();
      const tooltipRect = tooltipEl.getBoundingClientRect();
      const boundaryRect = boundaryEl.getBoundingClientRect();
      const toolbarMaxPoint = iconRect.left + tooltipRect.width;
      const boundaryMaxPoint = boundaryRect.left + boundaryRect.width;
      if (toolbarMaxPoint + TOOLTIP_CLIPPING_BUFFER > boundaryMaxPoint) {
        return true;
      }
    }
    return false;
  };

  isTooltipClippingY = (mouseEvent) => {
    const targetEl = ReactDOM.findDOMNode(mouseEvent.target);
    const pageEl = ReactDOM.findDOMNode(this.tooltipEl).closest("[data-chart]").parentNode; // find the chart parent, page is the parent of that

    const targetRect = targetEl.getBoundingClientRect();
    const pageRect = pageEl.getBoundingClientRect();

    if (targetRect.top - this.getTooltipHeight() - TOOLTIP_OFFSET_Y_TOP - TOOLTIP_CLIPPING_BUFFER < pageRect.top) {
      return true;
    }
    return false;
  };

  render() {
    const { children, label, text } = this.props;
    const { show, left, top, wrap } = this.state;
    let content = label ? helpers[normalizeLabel(label)] : text;

    // we return the children only if helper has no content to show
    if (!content) return <span>{children}</span>;

    // dangerouslySetInnerHTML is used for basic formatting within the tooltip
    return (
      <Container ref={(element) => (this.rootEl = element)}>
        <Inner
          tabIndex="0"
          ref={(element) => (this.iconEl = element)}
          onMouseEnter={this.showTooltip}
          onMouseMove={this.showTooltip}
          onMouseLeave={this.hideTooltip}
          onFocus={this.showTooltip}
          onBlur={this.hideTooltip}
          data-helper
        >
          {children}
          <Tooltip
            ref={(element) => (this.tooltipEl = element)}
            show={show}
            style={{left, top}}
            wrap={wrap}
            onMouseEnter={this.hideTooltip}
          >
            <Text type='label' color="white" as='span'>
              <span dangerouslySetInnerHTML={{__html: content}} />
            </Text>
          </Tooltip>
        </Inner>
      </Container>
    );
  }
}

export default Helper;
