/* eslint-disable unused-imports/no-unused-vars */
import * as React from "react";
import * as R from "ramda";

import { FocusableItemNative } from "@applicaster/zapp-react-native-ui-components/Components/NativeFocusables";
import { BaseFocusable } from "@applicaster/zapp-react-native-ui-components/Components/BaseFocusable/index.ios";

import {
  focusManager,
  forceFocusableFocus,
} from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
import { findNodeHandle } from "react-native";

function noop() {}

type Props = {
  id: string;
  groupId: string;
  onPress?: (nativeEvent: React.SyntheticEvent) => void;
  onFocus?: (nativeEvent: React.SyntheticEvent) => void;
  onBlur?: (nativeEvent: React.SyntheticEvent) => void;
  children: (focused?: boolean) => React.ReactNode;
  isParallaxDisabled: boolean;
  preferredFocus?: boolean;
  selected?: boolean;
  isFocusable?: boolean;
  nextFocusDown?: import("react").RefObject<any>;
  nextFocusUp?: import("react").RefObject<any>;
  nextFocusLeft?: import("react").RefObject<any>;
  nextFocusRight?: import("react").RefObject<any>;
  nextTvosFocusLeft?: import("react").RefObject<any>;
  nextTvosFocusRight?: import("react").RefObject<any>;
  nextTvosFocusUp?: import("react").RefObject<any>;
  nextTvosFocusDown?: import("react").RefObject<any>;
  forceFocus?: boolean;
  initialFocus?: boolean;
  onLayout?: (e: any) => void;
};

export class Focusable extends BaseFocusable<Props> {
  private nextFocusableReactTags: Record<string, number>;
  constructor(props) {
    super(props);
    this.isGroup = false;

    this.onPress = this.onPress.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.nextFocusableReactTags = {};
    this.preferredFocus = this.preferredFocus.bind(this);
    this.measureView = this.measureView.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();
  }

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

  onPress({ nativeEvent }) {
    const { onPress = noop } = this.props;
    onPress(nativeEvent);
  }

  onFocus({ nativeEvent }) {
    const { onFocus = noop } = this.props;
    this.setState({ focused: true });
    const currentFocusGroupId = focusManager.getCurrentGroup()?.props?.groupId;

    if (!nativeEvent?.itemID?.includes(currentFocusGroupId)) {
      focusManager.setFocus(nativeEvent?.itemID, undefined, {
        groupFocusedChanged: true,
      });
    }

    onFocus(nativeEvent);
  }

  onBlur({ nativeEvent }) {
    const { onBlur = noop } = this.props;
    this.setState({ focused: false });

    onBlur(nativeEvent);
  }

  setFocus(direction, callback) {
    const focusMethods = [
      { method: this.willReceiveFocus },
      { method: this.focus, args: [callback] },
      { method: this.hasReceivedFocus },
    ];

    const self = this;

    return R.reduce(
      (sequence, { method, args = [] }) => {
        return sequence
          .then(() => {
            return method.apply(self, args);
          })
          .catch((e) => {
            throw e;
          });
      },
      Promise.resolve(),
      focusMethods
    );
  }

  focus(callback) {
    const { groupId, id } = this.props;
    forceFocusableFocus(groupId, id, callback);
  }

  blur() {}

  focusableTags({
    nextTvosFocusLeft,
    nextTvosFocusRight,
    nextTvosFocusUp,
    nextTvosFocusDown,
  }) {
    const retVal: Record<string, number> = {};

    const addFocusNodeIfNeeded = (ref, type) => {
      const current = ref?.current;

      if (current) {
        const node = findNodeHandle(current);

        if (node) {
          retVal[type] = node;
        }
      }
    };

    addFocusNodeIfNeeded(nextTvosFocusDown, "nextTvosFocusDown");
    addFocusNodeIfNeeded(nextTvosFocusUp, "nextTvosFocusUp");
    addFocusNodeIfNeeded(nextTvosFocusLeft, "nextTvosFocusLeft");
    addFocusNodeIfNeeded(nextTvosFocusRight, "nextTvosFocusRight");

    return retVal;
  }

  componentDidUpdate() {
    const {
      nextTvosFocusLeft,
      nextTvosFocusRight,
      nextTvosFocusUp,
      nextTvosFocusDown,
    } = this.props;

    this.nextFocusableReactTags = this.focusableTags({
      nextTvosFocusLeft,
      nextTvosFocusRight,
      nextTvosFocusUp,
      nextTvosFocusDown,
    });
  }

  render() {
    const {
      children,
      groupId,
      id,
      isParallaxDisabled,
      isFocusable,
      // This done not to pass this props to native component, because it is not supported
      nextFocusDown,
      nextFocusUp,
      nextFocusLeft,
      nextFocusRight,
      nextTvosFocusLeft,
      nextTvosFocusRight,
      nextTvosFocusUp,
      nextTvosFocusDown,
      ...otherProps
    } = this.props;

    const { focused } = this.state;

    return (
      <FocusableItemNative
        itemId={groupId && id ? id : null}
        groupId={groupId && id ? groupId : null}
        isParallaxDisabled={isParallaxDisabled}
        onViewFocus={this.onFocus}
        onViewPress={this.onPress}
        onViewBlur={this.onBlur}
        ref={this.ref}
        onLayout={() => this.measureView()}
        focusable={isFocusable}
        {...this.nextFocusableReactTags}
        {...otherProps}
      >
        {R.is(Function, children) ? children(focused) : children}
      </FocusableItemNative>
    );
  }
}
