import React, { Fragment, Ref } from 'react';
import { findDOMNode } from 'react-dom';
import TooltipJS, { Options } from 'tooltip.js';
import { styled } from '../../styles';
import { GlobalStyles, tooltipStyles, getVariant } from './styles';

export type variants = 'primary' | 'secondary' | 'raw';

export interface Props extends Options {
  variant?: variants;
  role?: string;
  children: React.ReactNode;
  className?: string;
  ref?: Ref<any>;
}

export const defaultProps = {
  role: 'group',
  placement: 'top',
  variant: 'primary' as variants,
};

export class TooltipComponent extends React.Component<Props> {
  public static defaultProps = defaultProps;

  private popper?: TooltipJS;

  public containerRef: React.RefObject<HTMLDivElement> = React.createRef();

  public getOptions = (): Options => {
    const { placement, role, children, ...otherProps } = this.props;
    const popover = this.getPopover() as HTMLElement;

    return {
      placement,
      title: popover,
      html: true,
      container: document.body,
      popperOptions: {
        modifiers: {
          setState: {
            order: 800,
            enabled: true,
            fn: this.modifier,
          },
        },
      },
      ...otherProps,
    };
  };

  private initPopper = () => {
    if (!this.popper) {
      const popover = this.getPopover();
      const parentNode = popover.parentNode as HTMLElement;

      if (parentNode && getComputedStyle(parentNode).display !== 'none') {
        this.popper = new TooltipJS(parentNode, this.getOptions());
      }
    }
  };

  private destroyPopper = () => {
    if (this.popper) {
      this.popper.dispose();
      this.popper = undefined;
    }
  };

  public componentDidMount() {
    this.initPopper();
  }

  public componentWillUnmount() {
    this.destroyPopper();
  }

  public componentDidUpdate(
    prevProps: Props & Readonly<{ children?: React.ReactNode }>
  ) {
    if (
      prevProps.placement !== this.props.placement ||
      prevProps.children !== this.props.children
    ) {
      this.destroyPopper();
      this.initPopper();
    }
  }

  public hide() {
    this.popper && this.popper.hide();
  }

  public render() {
    const { role, children, className } = this.props;
    return (
      <Fragment>
        <GlobalStyles />
        <div ref={this.containerRef} className={className} role={role}>
          {children}
        </div>
      </Fragment>
    );
  }

  private getPopover = () => findDOMNode(this) as NodeSelector & Node & Element;

  // We can't type this because we would need to add `popper.js` as dependency
  private modifier = (data: any) => {
    // eslint-disable-next-line no-param-reassign
    data.arrowStyles.color = getComputedStyle(
      this.getPopover()
    ).backgroundColor;
    return data;
  };
}

export const Tooltip = styled(TooltipComponent)<Props>(
  tooltipStyles,
  getVariant
);
