import * as React from "react";

import { BaseFocusable } from "../BaseFocusable";
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";

type Props = {
  initialFocus?: boolean;
  id: string;
  groupId: string;
  node: {};
  preferredFocus?: boolean;
  onFocus?: () => void;
  onBlur?: () => void;
  onPress?: () => void;
  onPressIn?: () => void;
  onPressOut?: () => void;
  onLongPress?: () => void;
  handleFocus?: ({ mouse }: { mouse: boolean }) => void;
  children: (boolean, string) => React.ComponentType<any>;
  selected?: boolean;
  style?: React.CSSProperties;
};

class Focusable extends BaseFocusable<Props> {
  isGroup: boolean;
  mouse: boolean;
  longPressTimeout = null;

  constructor(props) {
    super(props);

    this.isGroup = false;
    this.preferredFocus = this.preferredFocus.bind(this);
    this.onMouseLeave = this.onMouseLeave.bind(this);
    this.onMouseEnter = this.onMouseEnter.bind(this);
    this.pressOut = this.pressOut.bind(this);
    this.pressIn = this.pressIn.bind(this);
    this.startLongPressTimeout = this.startLongPressTimeout.bind(this);
    this.resetLongPressTimeout = this.resetLongPressTimeout.bind(this);
    this.longPress = this.longPress.bind(this);
    this.press = this.press.bind(this);
  }

  /**
   * indicates whether the underlying component should claim preferred focus
   * when navigating into the group of this item
   * @returns {boolean}
   */
  isPreferredFocus() {
    return this.preferredFocus();
  }

  startLongPressTimeout() {
    this.longPressTimeout = setTimeout(() => {
      this.longPress(null);
    }, LONG_KEY_PRESS_TIMEOUT);
  }

  resetLongPressTimeout() {
    if (this.longPressTimeout) {
      try {
        clearTimeout(this.longPressTimeout);
      } catch {
        this.longPressTimeout = null;
      }
    }
  }

  /**
   * will invoke the underlying component's press method
   * @param {Object} keyEvent
   * @returns {Promise}
   */
  press(keyEvent: Nullable<React.MouseEvent>) {
    return this.onPress(keyEvent);
  }

  longPress(keyEvent: Nullable<React.MouseEvent>) {
    return this.onLongPress(keyEvent);
  }

  pressOut(keyEvent: Nullable<React.MouseEvent>) {
    this.resetLongPressTimeout();

    return this.onPressOut(keyEvent);
  }

  preferredFocus() {
    return this.props.preferredFocus || false;
  }

  pressIn(keyEvent: Nullable<React.MouseEvent>) {
    this.startLongPressTimeout();

    return this.onPressIn(keyEvent);
  }

  onMouseEnter() {
    const { id } = this.props;

    if (id !== "search_input_group_id") {
      this.mouse = true;
      this.props?.handleFocus?.({ mouse: true });

      focusManager.blur(null);
      focusManager.setFocus(this.getId(), null);

      this.setFocus(null);
    }
  }

  onMouseLeave() {
    const { id } = this.props;

    if (id !== "search_input_group_id") {
      this.mouse = false;
      this.blur(null);
    }
  }

  render() {
    const { children, style } = this.props;
    const { focused } = this.state;

    const id = this.getId();
    const focusableId = `focusable-${id}`;

    return (
      <div
        id={focusableId}
        ref={this.ref}
        onMouseEnter={this.onMouseEnter}
        onMouseLeave={this.onMouseLeave}
        onClick={this.press}
        onMouseDown={this.pressIn}
        onMouseUp={this.pressOut}
        data-testid={focusableId}
        focused-teststate={focused ? "focused" : "default"}
        style={style}
      >
        {children(focused, { mouse: this.mouse })}
      </div>
    );
  }
}

const FocusableWithFocusableContext = withFocusableContext(Focusable);

export { FocusableWithFocusableContext as Focusable };
