import * as React from "react";
import { join, merge, path } from "ramda";
import { View } from "react-native";

import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
import { FocusableCell } from "@applicaster/zapp-react-native-ui-components/Components/FocusableCell";
import { getItemType } from "@applicaster/zapp-react-native-utils/navigationUtils";
import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
import { sendSelectCellEvent } from "@applicaster/zapp-react-native-utils/analyticsUtils";
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
import { CellFocusedStateContextProvider } from "@applicaster/zapp-react-native-ui-components/Contexts/CellFocusedStateContext";

import { CellWithFocusable } from "./CellWithFocusable";
import { BaseFocusable } from "../BaseFocusable";
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
import { styles } from "./styles";

type Props = {
  dataLength: number;
  item: ZappEntry;
  index: number;
  shouldScrollHorizontally: (arg1: [any]) => boolean | null | undefined;
  shouldScrollVertically: (...args: any) => boolean | null | undefined;
  riverOffsetUpdater: (config: {
    focusable: BaseFocusable<any>;
    componentAnchorPointY: number;
    headerOffset?: number;
    extraAnchorPointYOffset?: number;
    componentPaddingTop?: number;
  }) => void;
  offsetUpdater: (arg1: string, arg2: number, arg3: number) => number;
  componentId: string;
  component: {
    id: string;
    styles?: {
      component_padding_top?: number;
    };
  };
  selected?: boolean;
  CellRenderer: React.FunctionComponent<any> & {
    hasFocusableInside: (item: ZappEntry) => boolean;
  };
  preferredFocus: boolean;
  navigator: {
    push: (arg1: Record<any, any>) => void;
  };
  onFocus: (focusable?: any, mouse?: any) => void | null | undefined;
  onBlur: (arg1?: any) => void | null | undefined;
  onPress: (arg1?: any, index?: any) => void | null | undefined;
  willReceiveFocus: (arg1?: any) => void | null | undefined;
  hasReceivedFocus: (arg1?: any) => void | null | undefined;
  hasHeader: boolean;
  isScreenWrappedInContainer: boolean | null | undefined;
  sendOnClickEvent: () => void;
  scrollToIndex: (index: number) => void;
  appData: {
    layoutVersion: "v1" | "v2";
  };
  componentAnchorPointY: number; // | null | undefined;
  screenLayout: any;
  getHeaderOffset: () => {
    headerOffset?: number;
    headersAbove?: number;
  };
  focused: boolean;
  groupId: string;
  isFocusable: boolean;
  skipFocusManagerRegistration?: boolean;
  shouldUpdate: boolean;
  behavior: Behavior;
  componentsMapOffset: number;
};

type State = {
  hasFocusableInside: boolean;
  cellFocused: boolean;
};

export class CellComponent extends React.Component<Props, State> {
  accessibilityManager: AccessibilityManager;

  constructor(props) {
    super(props);
    this.onPress = this.onPress.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.willReceiveFocus = this.willReceiveFocus.bind(this);
    this.hasReceivedFocus = this.hasReceivedFocus.bind(this);
    this.scrollVertically = this.scrollVertically.bind(this);
    this.scrollToIndex = this.scrollToIndex.bind(this);
    this.handleAccessibilityFocus = this.handleAccessibilityFocus.bind(this);

    this.state = {
      hasFocusableInside: props.CellRenderer.hasFocusableInside?.(props.item),
      cellFocused: false,
    };

    this.accessibilityManager = AccessibilityManager.getInstance();
  }

  setScreenLayout(componentAnchorPointY, screenLayout) {
    if (componentAnchorPointY && screenLayout) {
      const updateLayout =
        componentAnchorPointY !== screenLayout?.componentAnchorPointY;

      updateLayout && screenLayout?.setScreenLayout({ componentAnchorPointY });
    }
  }

  onPress() {
    const { onPress, item, index } = this.props;

    if (onPress) {
      onPress(item, index);
    } else {
      const { item, navigator, component, appData } = this.props;
      const itemTargetScreen = path(["screen_type"], item);
      const componentTargetScreen = path(["data", "target"], component);
      const itemType = getItemType(item, appData?.layoutVersion);

      sendSelectCellEvent(item, component, item.title, index);

      if (
        itemType !== SCREEN_TYPES.PLAYABLE &&
        componentTargetScreen &&
        !itemTargetScreen
      ) {
        navigator.push(merge(item, { screen_type: componentTargetScreen }));

        return;
      }

      navigator.push(item);
    }
  }

  onFocus(focusable, mouse) {
    const {
      item: { id, title },
      shouldScrollVertically,
      shouldUpdate,
      isFocusable = true,
    } = this.props;

    if (isFocusable) {
      this.setState({ cellFocused: true });

      if (
        shouldUpdate &&
        shouldScrollVertically?.(mouse, focusable, id, title)
      ) {
        this.scrollVertically(focusable);
      }
    }
  }

  onBlur() {
    this.setState({ cellFocused: false });
  }

  willReceiveFocus() {}

  hasReceivedFocus() {}

  scrollVertically(focusable) {
    // TODO | https://applicaster.monday.com/boards/3659728745/pulses/5873048938 | refactor to remove riverOffsetUpdater, componentAnchorPointY, screenLayout, getHeaderOffset,
    const {
      riverOffsetUpdater,
      componentAnchorPointY,
      screenLayout,
      getHeaderOffset,
    } = this.props;

    if (!focusable?.mouse) {
      const { headerOffset } = getHeaderOffset();

      const extraAnchorPointYOffset =
        screenLayout?.extraAnchorPointYOffset || 0;

      riverOffsetUpdater({
        focusable,
        componentAnchorPointY,
        headerOffset,
        extraAnchorPointYOffset,
        componentPaddingTop:
          this.props?.component?.styles?.component_padding_top,
      });
    }
  }

  scrollToIndex(focusable: { mouse?: boolean } = {}) {
    return (index: number) => {
      if (!focusable?.mouse) {
        this.props?.scrollToIndex?.(index);
      }
    };
  }

  isCellFocused(focusableFocused) {
    const { focused, isFocusable = true } = this.props;

    return !isFocusable ? false : focused || focusableFocused;
  }

  handleAccessibilityFocus(index, dataLength) {
    // For loop scrolling, calculate the correct logical index
    const logicalIndex = dataLength ? index % dataLength : index;

    const positionLabel = dataLength
      ? `item ${logicalIndex + 1} of ${dataLength}`
      : "";

    if (this.state.hasFocusableInside) {
      this.accessibilityManager.readText({
        text: " ",
      });
    } else if (this.state.cellFocused) {
      this.accessibilityManager.readText({
        text: `${positionLabel}`,
      });
    }
  }

  componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>) {
    if (prevProps.item !== this.props.item) {
      this.setState({
        hasFocusableInside: this.props.CellRenderer.hasFocusableInside?.(
          this.props.item
        ),
      });
    }

    if (
      prevState.cellFocused !== this.state.cellFocused ||
      this.state.hasFocusableInside
    ) {
      this.handleAccessibilityFocus(this.props.index, this.props.dataLength);
    }
  }

  render() {
    const {
      item,
      index,
      component,
      offsetUpdater,
      preferredFocus,
      CellRenderer,
      onFocus,
      onBlur,
      willReceiveFocus,
      hasReceivedFocus,
      groupId,
      isFocusable = true,
      skipFocusManagerRegistration,
      behavior,
    } = this.props;

    const { id } = item;
    const focusableId = join("-", [component?.id, id, index]);

    const handleFocus = (focusable, mouse) => {
      const focusFn = onFocus || noop;
      focusFn(focusable, mouse);
      this.onFocus(focusable, mouse);
    };

    if (this.state.hasFocusableInside) {
      return (
        <CellFocusedStateContextProvider cellFocused={this.state.cellFocused}>
          <CellWithFocusable
            CellRenderer={CellRenderer}
            item={item}
            id={focusableId}
            groupId={groupId || component?.id}
            onFocus={handleFocus}
            onBlur={onBlur || this.onBlur}
            index={index}
            scrollTo={this.scrollToIndex()}
            isFocusable={isFocusable}
            skipFocusManagerRegistration={skipFocusManagerRegistration}
            behavior={behavior}
          />
        </CellFocusedStateContextProvider>
      );
    }

    return (
      <CellFocusedStateContextProvider cellFocused={this.state.cellFocused}>
        <View
          testID={`${component?.id}-${id}`}
          accessible={false}
          style={styles.touchableCell}
        >
          <Focusable
            id={focusableId}
            groupId={groupId || component?.id}
            onFocus={handleFocus}
            onBlur={onBlur || this.onBlur}
            onPress={this.onPress}
            willReceiveFocus={willReceiveFocus || this.willReceiveFocus}
            hasReceivedFocus={hasReceivedFocus || this.hasReceivedFocus}
            preferredFocus={preferredFocus}
            offsetUpdater={offsetUpdater}
            style={styles.baseCell}
            isFocusable={isFocusable}
            skipFocusManagerRegistration={skipFocusManagerRegistration}
            {...this.accessibilityManager.getButtonAccessibilityProps(
              item?.extensions?.accessibility?.label || item?.title
            )}
          >
            {(focused, event) => {
              const isFocused = this.isCellFocused(focused);

              return (
                <FocusableCell
                  {...{
                    index,
                    CellRenderer,
                    item,
                    focused: isFocused,
                    scrollTo: this.scrollToIndex(event),
                    behavior,
                  }}
                />
              );
            }}
          </Focusable>
        </View>
      </CellFocusedStateContextProvider>
    );
  }
}
