/*
Description: This is a React Native Visibility Sensor Component,
which helps in detecting whether a given component is visible within the viewport or not.
It is useful for implementing lazy loading or triggering specific actions when a component comes into view.
*/

import React, { useEffect, useState, useRef, ReactNode, FC } from "react";
import { View, Dimensions } from "react-native";
import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";

export interface IDimensionData {
  rectTop: number;
  rectBottom: number;
  rectWidth: number;
  fullWidth: number;
}

export interface Props {
  /** The component that needs to be in the viewport */
  children: ReactNode;
  checkPlayerPosition: ({ position, isFullyVisible }) => void;
  onViewportEnter: ({ dimensions, window }) => void;
  onViewportLeave: ({ dimensions, window }) => void;
}

const RNView = View as any;

const VisibilitySensorComponent: FC<Props> = ({
  children,
  checkPlayerPosition,
  onViewportEnter,
  onViewportLeave,
}: Props) => {
  const myView: any = useRef(null);
  const [lastValue, setLastValue] = useState<boolean>(false);

  const [dimensions, setDimensions] = useState<IDimensionData>({
    rectTop: 0,
    rectBottom: 0,
    rectWidth: 0,
    fullWidth: 0,
  });

  const isRTL: boolean = useIsRTL();

  let interval: any = null;

  const startWatching = () => {
    if (interval) {
      return;
    }

    interval = setInterval(() => {
      if (!myView || !myView.current) {
        return;
      }

      // TODO: Not the best way. When not in viewport passed random values
      myView.current.measure(
        async (
          _x: number,
          _y: number,
          width: number,
          height: number,
          pageX: number,
          pageY: number
        ) => {
          setDimensions({
            rectTop: pageY,
            rectBottom: pageY + height,
            rectWidth: pageX + width,
            fullWidth: width,
          });
        }
      );
    }, 1000);
  };

  const stopWatching = () => {
    interval = clearInterval(interval);
  };

  const isInViewPort = () => {
    const window = Dimensions.get("window");

    if (dimensions.fullWidth === 0) {
      return;
    }

    const isFullyVisible =
      dimensions.rectBottom !== 0 &&
      dimensions.rectTop > 0 &&
      dimensions.rectBottom <= window.height &&
      dimensions.rectWidth >= dimensions.fullWidth &&
      window.width >= dimensions.rectWidth;

    const widthOffset = isRTL
      ? window.width - dimensions.rectWidth
      : dimensions.rectWidth;

    const xOffset = Math.round((widthOffset / window.width) * 100) / 100;

    const yOffset =
      Math.round((dimensions.rectTop / window.height) * 100) / 100;

    if (lastValue !== isFullyVisible) {
      setLastValue(isFullyVisible);

      if (isFullyVisible) {
        onViewportEnter({ dimensions, window });
      } else {
        onViewportLeave({ dimensions, window });
      }
    }

    checkPlayerPosition({ isFullyVisible, position: { xOffset, yOffset } });
  };

  useEffect(() => {
    if (!isTV()) {
      startWatching();
      isInViewPort();

      return stopWatching;
    }
  }, [dimensions.rectTop, dimensions.rectBottom, dimensions.rectWidth]);

  const flexStyle = { flex: 1 };

  return (
    <RNView collapsable={false} ref={myView} style={flexStyle}>
      {children}
      <View />
    </RNView>
  );
};

const VisibilitySensor = React.memo<Props>(VisibilitySensorComponent);

export default VisibilitySensor;
