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 { CellWithFocusable } from "./CellWithFocusable";
import { BaseFocusable } from "../BaseFocusable";
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";

type Props = {
  item: {
    id: string;
    title: string;
  };
  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;
  }) => void;
  offsetUpdater: (arg1: string, arg2: number, arg3: number) => number;
  componentId: string;
  component: {
    id: string;
  };
  selected?: boolean;
  CellRenderer: React.ComponentType<{
    item: any;
    state: string;
  }> & { hasFocusableInside: (item: Record<string, string>) => 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;
};

type State = {
  hasFocusableInside: boolean;
};

const baseCellStyles = {
  height: "100%",
  display: "flex",
  flex: 1,
} as const;

const touchableStyles = {
  flex: 1,
  height: "100%",
} as const;

export class CellComponent extends React.Component<Props, State> {
  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.state = {
      hasFocusableInside: props.CellRenderer.hasFocusableInside?.(props.item),
    };
  }

  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) {
      if (
        shouldUpdate &&
        shouldScrollVertically?.(mouse, focusable, id, title)
      ) {
        this.scrollVertically(focusable);
      }
    }
  }

  onBlur() {}

  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,
      });
    }
  }

  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;
  }

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

  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 (
        <CellWithFocusable
          CellRenderer={CellRenderer}
          item={item}
          id={focusableId}
          groupId={groupId || component?.id}
          onFocus={handleFocus}
          index={index}
          scrollTo={this.scrollToIndex()}
          isFocusable={isFocusable}
          skipFocusManagerRegistration={skipFocusManagerRegistration}
          behavior={behavior}
        />
      );
    }

    return (
      <View
        testID={`${component?.id}-${id}`}
        accessible={false}
        style={touchableStyles}
      >
        <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={baseCellStyles}
          isFocusable={isFocusable}
          skipFocusManagerRegistration={skipFocusManagerRegistration}
        >
          {(focused, event) => {
            const isFocused = this.isCellFocused(focused);

            if (isFocused) {
              const accessibilityManager = AccessibilityManager.getInstance();

              const accessibilityTitle =
                item?.extensions?.accessibility?.label || item?.title || "";

              const accessibilityHint =
                item?.extensions?.accessibility?.hint || "";

              accessibilityManager.readText({
                text: `${accessibilityTitle} ${accessibilityHint}`,
              });
            }

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