/* eslint-disable jsx-a11y/mouse-events-have-key-events */
import React, { MouseEvent, TouchEvent } from "react";
import { DefaultingInferableComponentEnhancer, hoistStatics } from "recompose";

interface InjectedProps {
  onMouseOver: React.EventHandler<MouseEvent<any>>;
  onMouseOut: React.EventHandler<MouseEvent<any>>;
  onTouchEnd: React.EventHandler<TouchEvent<any>>;
  mouseHover: boolean;
}

/**
 * withMouseHover provides a property `MouseHover: boolean`
 * to indicate a that the mouse is hovering the element.
 */
const withMouseHover: DefaultingInferableComponentEnhancer<
  InjectedProps
> = hoistStatics<InjectedProps>(
  <T extends InjectedProps>(BaseComponent: React.ComponentType<T>) => {
    // TODO: (cvle) This is a workaround for a typescript bug
    // https://github.com/Microsoft/TypeScript/issues/30762
    const Workaround = BaseComponent as React.ComponentType<InjectedProps>;

    class WithMouseHover extends React.Component<InjectedProps> {
      private lastTouchEndTime = 0;
      public state = {
        mouseHover: false,
      };

      private handleTouchEnd: React.EventHandler<TouchEvent<any>> = event => {
        if (this.props.onTouchEnd) {
          this.props.onTouchEnd(event);
        }
        this.lastTouchEndTime = new Date().getTime();
      };

      private handleMouseOver: React.EventHandler<MouseEvent<any>> = event => {
        if (this.props.onMouseOver) {
          this.props.onMouseOver(event);
        }
        const now = new Date().getTime();
        if (now - this.lastTouchEndTime > 750) {
          this.setState({ mouseHover: true });
        }
      };

      private handleMouseOut: React.EventHandler<MouseEvent<any>> = event => {
        if (this.props.onMouseOut) {
          this.props.onMouseOut(event);
        }
        this.setState({ mouseHover: false });
      };

      public render() {
        return (
          <Workaround
            {...this.props}
            onMouseOver={this.handleMouseOver}
            onMouseOut={this.handleMouseOut}
            onTouchEnd={this.handleTouchEnd}
            mouseHover={this.state.mouseHover}
          />
        );
      }
    }

    return WithMouseHover as React.ComponentType<any>;
  }
);

export default withMouseHover;
