import React, { useContext, useEffect, useLayoutEffect, useRef } from 'react';
import { Alert, InteractionManager } from 'react-native';
import { useHeaderHeight } from '@react-navigation/elements';
import { ScreenTrackingParams } from './types';
import Navigation from './Navigation';
import { ApplicationContext, MiniAppContext, ScreenContext } from '../Context';
import { GridSystem } from '../Layout';
import { version } from '../package.json';
import { useAppState } from './utils';
import { TooltipPortalHost, TooltipPortalProvider } from './TooltipPortal.tsx';

const runAfterInteractions = InteractionManager.runAfterInteractions;

/**
 * container for stack screen
 * @param props
 * @constructor
 */
const StackScreen: React.FC<any> = props => {
  const { showGrid, navigator } = useContext(ApplicationContext);
  const tracking = useRef<any>({
    mounted: false,
    timeoutLoad: undefined,
    timeoutInteraction: undefined,
    timeoutTracking: undefined,
    timeoutLoading: undefined,
    startTime: Date.now(),
    endTime: Date.now(),
    traceIdLoad: undefined,
    traceIdInteraction: undefined,
    releaseLoad: undefined,
    releaseInteraction: undefined,
    releaseUserInteraction: undefined,
    releaseLoading: false,
    timeLoad: 0,
    timeInteraction: 0,
    timeStartLoading: 0,
    timeEndLoading: 0,
    widgets: [],
    params: undefined,
    lastElement: undefined,
  });
  const widgets = useRef<any>([]);
  const context = useContext<any>(MiniAppContext);
  const {
    screen: Component,
    options,
    initialParams,
    bottomTab,
  } = props.route.params;
  const navigation = useRef(new Navigation(props.navigation, context)).current;
  const heightHeader = useHeaderHeight();
  const { isBackgroundToForeground } = useAppState();

  const data = {
    ...initialParams,
    ...props.route.params,
    navigation,
  };

  delete data.screen;
  delete data.initialParams;

  const screenName = Component?.name || Component?.type?.name || 'Invalid';

  /**
   * set options for screen
   */
  useLayoutEffect(() => {
    if (options) {
      navigation.setOptions(options);
    }
  }, [navigation, options]);

  /**
   * tracking for screen
   */
  useEffect(() => {
    let focusScreen: any;
    let onFocusApp: any;
    if (['Invalid', 'screen'].includes(screenName)) {
      navigator?.maxApi?.showPopup?.('notice', {
        title: 'Invalid screen name',
        message:
          'Your screen has not been rendered because Platform has not detected the screen name. Please migrate to support this feature.',
      });
    }

    const startTime = Date.now();

    if (!bottomTab) {
      focusScreen = props.navigation.addListener('focus', () => {
        navigator?.maxApi?.getDataObserver?.('current_screen', (item: any) => {
          onScreenNavigated(item?.screenName, screenName);
          navigator?.maxApi?.setDataObserver?.('current_screen', {
            screenName,
            startTime,
          });
        });
      });
      onFocusApp = navigator?.maxApi?.listen?.('onFocusApp', () => {
        if (props.navigation.isFocused()) {
          navigator?.maxApi?.getDataObserver?.(
            'current_screen',
            (item: any) => {
              onScreenNavigated(item?.screenName, screenName);
              navigator?.maxApi?.setDataObserver?.('current_screen', {
                screenName,
                startTime,
              });
            },
          );
        }
      });
    }

    navigator?.maxApi?.startTraceScreenLoad?.(
      screenName,
      context,
      (item: any) => {
        tracking.current.traceIdLoad = item?.traceId;
      },
    );
    navigator?.maxApi?.startTraceScreenInteraction?.(
      screenName,
      context,
      (item: any) => {
        tracking.current.traceIdInteraction = item?.traceId;
      },
    );

    tracking.current.timeoutTracking = setTimeout(() => {
      onScreenLoad();
      onScreenInteraction();
    }, 5000);

    return () => {
      onScreenLoad();
      onScreenInteraction();
      clearTimeout(tracking.current.timeoutLoad);
      clearTimeout(tracking.current.timeoutInteraction);
      clearTimeout(tracking.current.timeoutTracking);
      // eslint-disable-next-line react-hooks/exhaustive-deps
      clearTimeout(tracking.current.timeoutLoading);
      focusScreen?.();
      onFocusApp?.remove?.();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const onScreenNavigated = (pre: string, current: string) => {
    if (!isBackgroundToForeground) {
      const item: any = {
        preScreenName: pre,
        screenName: current,
        componentName: 'Screen',
        state: 'navigated',
        action: tracking.current?.mounted ? 'back' : 'push',
      };

      context?.autoTracking?.({
        ...context,
        ...item,
      });

      tracking.current.mounted = true;

      /**
       * debug toast
       */
      navigator?.maxApi?.showToastDebug?.({
        appId: context.appId,
        message: `${screenName} screen_navigated`,
        type: 'ERROR',
      });
    }
  };

  /**
   * tracking for screen load
   */
  const onScreenLoad = () => {
    if (!tracking.current?.releaseLoad) {
      let timeLoad = tracking.current.timeLoad;
      if (timeLoad === 0) {
        timeLoad = tracking.current.endTime - tracking.current.startTime;
      }

      context?.autoTracking?.({
        ...context,
        screenName,
        componentName: 'Screen',
        state: 'load',
        duration: timeLoad,
        widgets: tracking.current.widgets,
        params: tracking.current.params,
        version: version,
        lastElement: tracking.current.lastElement,
      });
      navigator?.maxApi?.stopTrace?.(
        tracking.current.traceIdLoad,
        { value: timeLoad / 1000 },
        null,
      );
      tracking.current.releaseLoad = true;

      /**
       * debug
       */
      navigator?.maxApi?.showToastDebug?.({
        appId: context.appId,
        message: `${screenName} screen_load_time ${timeLoad}`,
        type: 'ERROR',
      });
      if (
        __DEV__ &&
        tracking.current.lastElement?.children?.current?.length > 0
      ) {
        Alert.alert(
          `${screenName}- load ${timeLoad}ms`,
          JSON.stringify(tracking.current.lastElement?.children?.current),
        );
      }
    }
  };

  /**
   * tracking for screen load
   */
  const onScreenInteraction = () => {
    if (!tracking.current?.releaseInteraction) {
      let timeLoad = tracking.current.timeLoad;
      if (timeLoad === 0) {
        timeLoad = tracking.current.endTime - tracking.current.startTime;
      }
      if (tracking.current.timeInteraction === 0) {
        tracking.current.timeInteraction = timeLoad;
      }

      context?.autoTracking?.({
        ...context,
        screenName,
        componentName: 'Screen',
        state: 'interaction',
        duration: tracking.current.timeInteraction - timeLoad,
        totalDuration: tracking.current.timeInteraction,
        params: tracking.current.params,
        version: version,
      });
      navigator?.maxApi?.stopTrace?.(
        tracking.current.traceIdInteraction,
        { value: tracking.current.timeInteraction / 1000 },
        null,
      );
      tracking.current.releaseInteraction = true;

      /**
       * debug toast
       */
      navigator?.maxApi?.showToastDebug?.({
        appId: context.appId,
        message: `${screenName} screen_interaction_time ${tracking.current.timeInteraction}`,
        type: 'ERROR',
      });
    }
  };

  /**
   * tracking for first interaction by user
   */
  const onFirstInteraction = (action: string) => {
    if (!tracking.current?.releaseUserInteraction) {
      const timeLoad = tracking.current.endTime - tracking.current.startTime;
      context?.autoTracking?.({
        ...context,
        screenName,
        componentName: 'Screen',
        state: 'interaction',
        duration: timeLoad,
        action,
      });
      tracking.current.releaseUserInteraction = true;

      /**
       * debug
       */
      navigator?.maxApi?.showToastDebug?.({
        appId: context.appId,
        message: `${screenName} user_interaction ${timeLoad}`,
        type: 'ERROR',
      });
    }
  };

  /**
   * tracking for loading screen
   */
  const onScreenLoading = () => {
    const start = tracking.current.timeStartLoading;
    const end = tracking.current.timeEndLoading;
    if (!tracking.current?.releaseLoading && start && end && end > start) {
      context?.autoTracking?.({
        ...context,
        screenName,
        componentName: 'Screen',
        state: 'loading',
        duration: end - start,
        loadingType: 'skeleton',
      });
      tracking.current.releaseLoading = true;
    }
  };

  return (
    <ScreenContext.Provider
      value={{
        screenName,
        onElementLoad: (item: any) => {
          /**
           * widget handle
           */
          if (item?.componentName === 'Widget') {
            const index = widgets.current.findIndex(
              (element: any) => element.componentId === item.componentId,
            );
            const time = Date.now() - tracking.current.startTime;
            if (index === -1) {
              widgets.current.push({
                componentId: item.componentId,
                appId: item.params?.appId,
                code: item.params?.code,
                start: time,
              });
            } else {
              const exist = widgets.current[index];
              widgets.current[index] = {
                ...exist,
                end: time,
                duration: time - exist.start,
              };
            }
            return;
          }

          /**
           * tracking for element screen load
           */
          clearTimeout(tracking.current.timeoutLoad);
          tracking.current.endTime = Date.now();
          tracking.current.timeoutInteraction?.cancel?.();
          tracking.current.timeoutInteraction = runAfterInteractions(() => {
            tracking.current.timeInteraction =
              Date.now() - tracking.current.startTime;
          });

          /**
           * support for debug last element
           */
          if (item?.componentName) {
            tracking.current.lastElement = item;
          }

          /**
           * for stop tracking when user interaction
           */
          if (item?.interaction) {
            onScreenLoad();
            onScreenInteraction();
            onFirstInteraction(item?.action);
          }
          /**
           * timeout for handle tracking screen
           */
          tracking.current.timeoutLoad = setTimeout(() => {
            const time = tracking.current.endTime - tracking.current.startTime;
            tracking.current.widgets = widgets.current;

            if (tracking.current.timeLoad === 0) {
              tracking.current.timeLoad = time;
            }

            onScreenLoad();
            onScreenInteraction();
          }, 2000);
        },
        onLoading: (loading: boolean) => {
          if (loading && tracking.current.timeStartLoading === 0) {
            tracking.current.timeStartLoading = Date.now();
          }

          /**
           * tracking for loading screen
           */
          if (!loading) {
            tracking.current.timeEndLoading = Date.now();
          }

          /**
           * timeout for handle tracking screen
           */
          clearTimeout(tracking.current.timeoutLoading);
          tracking.current.timeoutLoading = setTimeout(() => {
            onScreenLoading();
          }, 2000);
        },
        onSetParams: (item: ScreenTrackingParams) => {
          tracking.current.params = item;
        },
      }}
    >
      <TooltipPortalProvider>
        <Component heightHeader={heightHeader} {...data} />
        {showGrid && <GridSystem />}
        <TooltipPortalHost />
      </TooltipPortalProvider>
    </ScreenContext.Provider>
  );
};

export default StackScreen;
