import * as React from "react";
import * as R from "ramda";
import { View, StyleSheet } 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 { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
import { sendSelectCellEvent } from "@applicaster/zapp-react-native-utils/analyticsUtils";
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
import { CellWithFocusable } from "./CellWithFocusable";
import { getCellState } from "./utils";

const _getCellState = (focused, selected) =>
  getCellState({ focused, selected });

type Props = {
  item: ZappEntry;
  index: number;
  shouldScrollHorizontally: (
    focusable: any,
    id: string,
    title: string
  ) => boolean | null | undefined;
  shouldScrollVertically: (
    focusable: any,
    id: string,
    title: string
  ) => boolean | null | undefined;
  riverOffsetUpdater: (arg1: string, arg2: number) => number;
  offsetUpdater: (arg1: string, arg2: number) => number;
  componentId: string;
  component: {
    id: number | string;
    component_type: string;
  };
  selected: boolean;
  CellRenderer: React.ComponentType<{
    item: any;
    state: string;
  }> & { hasFocusableInside: (item: any) => boolean };
  preferredFocus: boolean;
  navigator: {
    push: (arg1: Record<any, any>) => void;
  };
  onFocus: (arg1?: any, index?: number) => void | null | undefined;
  onBlur: (arg1?: any, index?: number) => void | null | undefined;
  onPress: (arg1?: any, index?: number) => void | null | undefined;
  willReceiveFocus: (arg1?: any, index?: number) => void | null | undefined;
  hasReceivedFocus: (arg1?: any, index?: number) => void | null | undefined;
  hasHeader: boolean;
  isScreenWrappedInContainer: boolean | null | undefined;
  sendOnClickEvent: () => void;
  scrollToIndex: (index: number) => void;
  appData: {
    layoutVersion: "v1" | "v2";
  };
  getHeaderOffset: () => {
    headerOffset: number;
    headersAbove: number;
  };
  componentAnchorPointY: number | null | undefined;
  screenLayout: any;
  headerOffset: number;
  headerOffsetOverride: number | null | undefined;
  focused: boolean;
  groupId: string;
  mainOffsetUpdater: (...args: any[]) => void | null | undefined;
  isFocusable: boolean;
  shouldUpdate: boolean;
  behavior: Behavior;
};

type State = {
  focusedId: string | null;
};

const styles = StyleSheet.create({
  container: { flex: 1 },
});

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

export class TvOSCellComponent extends React.Component<Props, State> {
  cell: any;
  target: any;
  layout: any;
  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.scrollTo = this.scrollTo.bind(this);
    this.state = { focusedId: null };
    this.cell = null;
    this.setInitialFocus = this.setInitialFocus.bind(this);
    this.setFocusId = this.setFocusId.bind(this);
    this.unsetFocusId = this.unsetFocusId.bind(this);
    this.onLayout = this.onLayout.bind(this);
  }

  onLayout({ nativeEvent }) {
    const { target, layout } = nativeEvent;
    this.target = target;
    this.layout = layout;
  }

  getFocusedId() {
    return `${this.props?.component?.id}-${this.props?.item?.id}`;
  }

  setInitialFocus() {
    this.setFocusId(this.getFocusedId());
  }

  setFocusId(id) {
    this.setState((prevState) => {
      if (prevState.focusedId === id) {
        return prevState;
      }

      return { focusedId: id };
    });
  }

  unsetFocusId() {
    const itemInFocus = focusManager.getCurrentFocus();
    itemInFocus.onBlur();

    this.setFocusId(null);
  }

  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 = R.path(["screen_type"], item);
      const componentTargetScreen = R.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(R.merge(item, { screen_type: componentTargetScreen }));

        return;
      }

      navigator.push(item);
    }
  }

  onFocus(focusable, _idx: number) {
    const {
      item: { id, title },
      shouldScrollVertically,
      screenLayout = null,
      componentAnchorPointY = null,
      mainOffsetUpdater,
      getHeaderOffset,
      groupId,
      component,
      index,
    } = this.props;

    this.setScreenLayout(componentAnchorPointY, screenLayout);
    this.setFocusId(id);

    if (
      this.props.shouldUpdate &&
      shouldScrollVertically?.(focusable, String(id), String(title))
    ) {
      const { headerOffset } = getHeaderOffset();

      const extraAnchorPointYOffset =
        screenLayout?.extraAnchorPointYOffset || 0;

      const marginTop = screenLayout?.screenMarginTop || 0;

      const totalOffset =
        headerOffset +
        (componentAnchorPointY || 0) +
        extraAnchorPointYOffset -
        marginTop;

      mainOffsetUpdater?.(
        { tag: this.target },
        totalOffset,
        null,
        groupId,
        id,
        component.id,
        index
      );
    }
  }

  onBlur() {
    this.setFocusId(null);
  }

  willReceiveFocus() {}

  hasReceivedFocus() {}

  scrollTo() {}

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

    const { id } = item;
    const focusedId = this.state.focusedId;
    const selectedCell = focusedId || selected;

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

    const handleFocus = (arg1: any, index: number) => {
      const focusFn = onFocus || noop;
      focusFn(arg1, index);

      this.onFocus(arg1, index);
    };

    const hasFocusableInside = CellRenderer.hasFocusableInside?.(item);

    if (hasFocusableInside) {
      return (
        <View onLayout={this.onLayout}>
          <CellWithFocusable
            CellRenderer={CellRenderer}
            item={item}
            id={focusableId}
            groupId={(groupId || component?.id).toString()}
            onFocus={handleFocus}
            index={index}
            scrollTo={this.scrollTo}
            preferredFocus={preferredFocus}
            focused={this.props.focused}
            behavior={behavior}
            isFocusable={isFocusable}
          />
        </View>
      );
    }

    return (
      <View onLayout={this.onLayout} style={styles.container}>
        <Focusable
          id={focusableId}
          groupId={groupId || component?.id}
          onFocus={handleFocus}
          onBlur={onBlur || this.onBlur}
          isParallaxDisabled={this.layout?.width > 1740}
          onPress={this.onPress}
          willReceiveFocus={willReceiveFocus || this.willReceiveFocus}
          hasReceivedFocus={hasReceivedFocus || this.hasReceivedFocus}
          preferredFocus={preferredFocus}
          offsetUpdater={offsetUpdater}
          style={baseCellStyles}
          isFocusable={isFocusable}
        >
          {(focused) => {
            return (
              <FocusableCell
                {...{
                  index,
                  CellRenderer,
                  item,
                  focused: this.props.focused || selectedCell || focused,
                  scrollTo: this.scrollTo,
                  behavior,
                }}
              />
            );
          }}
        </Focusable>
      </View>
    );
  }
}
