import * as React from "react";
import { NativeModules, StyleSheet, View } from "react-native";
import { getXray } from "@applicaster/zapp-react-native-utils/logger";

import { isApplePlatform, isWeb } from "../reactUtils";
import { useRivers } from "../reactHooks/state";

const layoutReducer = (state, { payload }) => {
  return state.map((item, index, _state) => ({
    height: index === payload.index ? payload.height : item.height,
    y:
      index > 0
        ? _state.slice(0, index).reduce((acc, value) => acc + value.height, 0)
        : 0,
  }));
};

type Props = {
  children: React.ReactNode;
  numberOfComponents: number;
  riverId: string;
};

type ItemProps = {
  children: React.ReactNode;
  index: number;
};

type MeasurementContext = {
  onLayout: (index: number) => (event) => void;
};

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

const { Logger } = getXray();

const logger = new Logger("general", "ui");

let notified = false;

export const MeasurementsSettersContext =
  React.createContext<MeasurementContext>({
    onLayout: function (_index): (event: any) => void {
      return undefined;
    },
  });

const moduleInitTime = Date.now();

const { ReactNativeEventBusBridge = null } = NativeModules;

export const ScreenLoadingMeasurements = ({
  children,
  numberOfComponents,
  riverId,
}: Props) => {
  const rivers = useRivers();
  const screenData = rivers?.[riverId];

  const [itemsLayout, dispatchItemLayout] = React.useReducer(
    layoutReducer,
    new Array(numberOfComponents).fill({ height: 0, y: 0 })
  );

  const [listHeight, setListHeight] = React.useState<number>(0);

  const onItemLayout = React.useCallback(
    (index) => (event) => {
      const { height } = event.nativeEvent.layout;

      if (!notified) {
        // Don't need to set state when event was already sent (optimization)
        dispatchItemLayout({ payload: { index, height } });
      }
    },
    []
  );

  const onListLayout = React.useCallback(
    (event) => {
      const { height } = event.nativeEvent.layout;
      const newValue = isWeb() ? 1080 : height;

      if (newValue !== listHeight) {
        setListHeight(isWeb() ? 1080 : height);
      }
    },
    [listHeight]
  );

  React.useEffect(() => {
    if (!notified) {
      const finishLoadingVisible =
        !!itemsLayout.find((item) => item.y > listHeight) ||
        itemsLayout.every((item) => item.height > 0);

      if (finishLoadingVisible) {
        if (screenData?.home) {
          if (isWeb()) {
            logger.sendAudienceEvent("app_home_loaded", {
              loading_duration: Date.now() - moduleInitTime,
            });
          } else {
            const event = isApplePlatform()
              ? {
                  type: "eventsBus.audience.appHomeLoaded",
                  source: "StartupTracker",
                  data: {
                    loading_duration: Date.now() - moduleInitTime,
                    timestamp: Date.now(),
                  },
                }
              : {
                  type: "StartupTracker.Milestone",
                  source: "StartupTracker",
                  data: {
                    id: "app_home_loaded",
                    data: {
                      loading_duration: Date.now() - moduleInitTime,
                    },
                    timestamp: Date.now(),
                  },
                };

            ReactNativeEventBusBridge?.postEvent(event);
          }
        }

        notified = true;
      }
    }
  }, [itemsLayout, listHeight]);

  const contextValue = React.useMemo(() => ({ onLayout: onItemLayout }), []);

  return (
    <MeasurementsSettersContext.Provider value={contextValue}>
      <View style={styles.container} onLayout={onListLayout}>
        {children}
      </View>
    </MeasurementsSettersContext.Provider>
  );
};

export const ScreenLoadingMeasurementsListItemWrapper = ({
  children,
  index,
}: ItemProps) => {
  const { onLayout } = React.useContext(MeasurementsSettersContext);

  return (
    <View style={styles.container} onLayout={onLayout?.(index)}>
      {children}
    </View>
  );
};
