/* eslint-disable @typescript-eslint/no-use-before-define */
/* eslint max-len: off */

import React, { useContext, useEffect, useMemo } from "react";
import { BackHandler, InteractionManager, NativeModules } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";

import {
  useContentTypes,
  usePickFromState,
} from "@applicaster/zapp-react-native-redux/hooks";
import { HooksManager } from "@applicaster/zapp-react-native-utils/appUtils/HooksManager";

import { legacyScreenData } from "@applicaster/quick-brick-core/App/NavigationProvider/utils";
import {
  LONG_KEY_PRESS_TIMEOUT,
  QUICK_BRICK_NAVBAR,
  QUICK_BRICK_CONTENT,
} from "@applicaster/quick-brick-core/const";
import { useZappHooksModalState } from "@applicaster/zapp-react-dom-ui-components/Components/ZappHooksModal/hooks/useZappHooksModalState";
import { ZappHookModalContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
import { HookModalContextT } from "@applicaster/zapp-react-native-ui-components/Contexts/ZappHookModalContext";
import { HOOKS_EVENTS } from "../../appUtils/HooksManager/constants";
import {
  getNavigationProps,
  getRiverFromRoute,
  getTargetRoute,
} from "../../navigationUtils";
import { platformSelect } from "../../reactUtils";
import { useConnectionInfo } from "../connection";
import { getTargetScreenData } from "../screen/useTargetScreenData";

import { isTV, isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
import { useNavbarState } from "../screen";
import { useNavigation } from "./useNavigation";
import { useModalStore } from "../../modalState";
import { ScreenDataContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenDataContext";

export { useNavigation } from "./useNavigation";

/**
 * This function helps to decide wether the navbar should be presented on the screen
 * based on route and / or screen Data
 *
 * Currently the scenarios include
 * - players => false
 * - search screen plugin => false
 * - hooks with `showNavBar: true` or `presentFullScreen` not set to true, defaults to no navbar for hook screens
 * - screens with `allow_screen_plugin_presentation` in general property => false
 *
 * @param {String} route current route of the screen
 * @param {Object} screenData payload associated with the currently presented screen
 * @returns {Boolean}
 */
export function isNavBarVisible(
  route: string,
  screenData: ZappRiver = {} as ZappRiver,
  showNavBar: boolean,
  canGoBack: boolean,
  videoModalState: QuickBrickVideoModalState | null = null
) {
  /* screenData is not actual navigator.data
   * or navigator.screenData,
   * but navigator.data.screen */
  if (route.includes("playable")) {
    return false;
  }

  if (
    videoModalState?.mode === "FULLSCREEN" &&
    videoModalState?.visible === true
  ) {
    return false;
  }

  if (screenData?.type === "qb_search_screen" && !isTV()) {
    return false;
  }

  if (screenData?.plugin_type === "login" && isTV()) {
    return false;
  }

  /* Match if screen is Hook */
  if (route.startsWith("/hooks/")) {
    const module = screenData?.module;

    if (module?.presentFullScreen) {
      return false;
    }

    return module?.showNavBar ?? false;
  }

  if (screenData?.hookPlugin) {
    const hookPlugin = screenData?.hookPlugin?.module;

    return (
      hookPlugin?.showNavBar || hookPlugin?.presentFullScreen !== true || false
    );
  }

  if (
    screenData?.general?.allow_screen_plugin_presentation ||
    screenData?.general?.hide_app_nav_bar
  ) {
    return false;
  }

  const hideSecondaryLevel = screenData?.navigations?.some(
    (navigation) => navigation.styles?.hide_secondary_level_menu
  );

  if (canGoBack && hideSecondaryLevel) {
    return false;
  }

  if (typeof showNavBar === "boolean" && !showNavBar) {
    return false;
  }

  return true;
}

const { ProfilerBridge } = NativeModules;

export const usePathname = () => React.useContext(PathnameContext);

export const useBackHandler = (cb: () => boolean) => {
  React.useEffect(() => {
    BackHandler.addEventListener("hardwareBackPress", cb);

    return () => {
      BackHandler.removeEventListener("hardwareBackPress", cb);
    };
  }, [cb]);
};

// starts with modal/
const isModalPathname = (pathname: string) => /^modal\//.test(pathname);

const isVideoModalPathname = (pathname: string) =>
  /^video-modal\//.test(pathname);

const isHookModalPathname = (pathname: string) =>
  /^hooks-modal\//.test(pathname);

const isHookPathname = (pathname: string) => /^\/hooks\//.test(pathname);

type VariousScreenData = LegacyNavigationScreenData | ZappRiver | ZappEntry;

export const useRoute = (): {
  screenData: VariousScreenData;
  pathname: string;
} => {
  const pathname = usePathname() || "";
  const navigator = useNavigation();
  const screenDataContext = legacyScreenData(useContext(ScreenDataContext));

  const { plugins, contentTypes, rivers } = usePickFromState([
    "plugins",
    "rivers",
    "contentTypes",
  ]);

  const { modalState } = useModalStore();
  const hookModalState = useZappHooksModalState();

  const modalScreenData = modalState.screen;

  const hookModalScreenData = hookModalState.state.screenData?.payload;

  const videoModalScreenData =
    navigator.videoModalState?.item &&
    legacyScreenData(
      {
        entry: navigator.videoModalState?.item as ZappEntry,
        screen: getTargetScreenData(
          navigator.videoModalState?.item as ZappEntry,
          rivers,
          contentTypes
        ),
      },
      plugins
    );

  // There are 4 route scenarios
  // For regular screens take date from navigator stack.
  // is path is model grab screenData from modal state
  // if path is video modal grab screenData from video state
  // if path is hook modal grab screenData from hook state
  // if path is hook grab screenData from screenData

  if (isModalPathname(pathname)) {
    const screenData = modalScreenData ?? ({} as ZappEntry);

    return { screenData, pathname };
  }

  if (isVideoModalPathname(pathname)) {
    const screenData = videoModalScreenData as LegacyNavigationScreenData;

    return { screenData, pathname };
  }

  if (isHookModalPathname(pathname)) {
    const screenData = hookModalScreenData;

    return { screenData, pathname };
  }

  if (isHookPathname(pathname)) {
    return { screenData: screenDataContext, pathname };
  }

  const screenData = screenDataContext;

  return { screenData, pathname };
};

type Callbacks = Partial<{
  handleHookPresent: ({
    route,
    payload,
  }: {
    route: string;
    payload: HookPluginProps;
  }) => void;
  handleHookError: (props: HookPluginProps & { error?: string }) => any;
  handleHookCancel: (props: Omit<HookPluginProps, "callback">) => any;
  handleHookComplete: (
    props: Omit<HookPluginProps, "callback"> & { route: string }
  ) => any;
}>;

export const useZappHooksForEntry = (
  entry: ZappEntry,
  callbacks?: Callbacks
) => {
  const {
    setState,
    resetState,
    setIsHooksExecutionInProgress,
    setIsPresentingFullScreen,
    setIsRunningInBackground,
  }: HookModalContextT = React.useContext(ZappHookModalContext.ReactContext);

  const {
    appData: { layoutVersion },
    rivers,
    plugins,
  } = usePickFromState(["appData", "rivers", "plugins"]);

  const contentTypes = useContentTypes();

  const isOnline = useConnectionInfo(true);

  useEffect(() => {
    resetState();

    if (entry) {
      setIsHooksExecutionInProgress(true);

      if (!isOnline) {
        setIsHooksExecutionInProgress(false);

        // TODO: ideally move this logic to hooks manager
        // @ts-ignore
        return callbacks?.handleHookComplete?.({ payload: entry });
      }

      const targetRoute = getTargetRoute(entry, "", {
        layoutVersion,
        contentTypes,
      });

      const targetScreen = getRiverFromRoute({ route: targetRoute, rivers });

      const hooksOptions = {
        rivers,
        plugins,
        targetScreen,
      };

      const handleHookPresent = ({ route, payload }) => {
        setIsPresentingFullScreen();

        setState({
          path: route,
          screenData: payload,
        });

        callbacks?.handleHookPresent?.({ route, payload });
      };

      const handleBackgroundHook = (hookProps) => {
        const { route, payload } = hookProps;

        setState({
          path: route,
          screenData: payload,
        });

        setIsRunningInBackground();
      };

      const handleHookError: Callbacks["handleHookError"] = (props) => {
        setIsHooksExecutionInProgress(false);
        callbacks?.handleHookError?.(props);
      };

      const handleHookCancel: Callbacks["handleHookCancel"] = (props) => {
        setIsHooksExecutionInProgress(false);
        callbacks?.handleHookCancel?.(props);
      };

      const handleHookComplete: Callbacks["handleHookComplete"] = (props) => {
        setIsHooksExecutionInProgress(false);
        callbacks?.handleHookComplete?.(props);
      };

      const hookManager = HooksManager(hooksOptions)
        .on(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, handleHookPresent)
        .on(HOOKS_EVENTS.ERROR, handleHookError)
        .on(HOOKS_EVENTS.CANCEL, handleHookCancel)
        .on(HOOKS_EVENTS.START_BACKGROUND_HOOK, handleBackgroundHook)
        .on(HOOKS_EVENTS.COMPLETE, handleHookComplete)
        .handleHooks(entry);

      return () => {
        hookManager
          .removeHandler(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, handleHookPresent)
          .removeHandler(HOOKS_EVENTS.ERROR, handleHookError)
          .removeHandler(HOOKS_EVENTS.CANCEL, handleHookCancel)
          .removeHandler(HOOKS_EVENTS.COMPLETE, handleHookComplete);
      };
    }
  }, [entry.id]);
};

export enum MenuTypes {
  drawer = "DRAWER",
  bottomTabBar = "BOTTOM_TAB_BAR",
}

export const useNavigationPluginData = (): ZappNavigation | undefined => {
  const {
    screenData: useRouteScreenData,
  }: { screenData: QuickBrickNavigationData | null } = useRoute();

  const navigations = useRouteScreenData?.targetScreen
    ? (useRouteScreenData.targetScreen as ZappRiver).navigations
    : (useRouteScreenData as ZappRiver).navigations;

  const navigationMenu = navigations?.find((nav) => nav.category === "menu");

  return navigationMenu;
};

export const useNavigationType = (): MenuTypes => {
  const navigationMenu = useNavigationPluginData();

  return !navigationMenu ||
    navigationMenu.navigation_type === "quick_brick_side_menu"
    ? MenuTypes.drawer
    : MenuTypes.bottomTabBar;
};

export const useProfilerLogging = () => {
  const logTimestamp = (data: string) =>
    InteractionManager.runAfterInteractions(() => {
      if (ProfilerBridge && data) {
        ProfilerBridge.updateTimer("navigation", data, Date.now());
      }
    });

  return logTimestamp;
};

export const useRunIfLongPress = (
  options = { timeout: LONG_KEY_PRESS_TIMEOUT }
) => {
  const pressTimeout =
    React.useRef<Nullable<ReturnType<typeof setTimeout>>>(null);

  const resetTimeout = () => {
    clearTimeout(pressTimeout.current as ReturnType<typeof setTimeout>);
  };

  const startTimeout = (cb: () => void) => {
    pressTimeout.current = setTimeout(cb, options.timeout);
  };

  const onPress = (cb: () => void) => {
    startTimeout(cb);
  };

  const onPressOut = () => {
    resetTimeout();
  };

  return { onPress, onPressOut };
};

/**
 * @returns boolean - navbar visiblity status
 */
export const useIsNavBarVisible = (): boolean => {
  const { visible } = useNavbarState();

  return useMemo(() => visible, [visible]);
};

const useGetNavBarTopBorderWidth = (): number => {
  const navigator = useNavigation();

  const navProps = getNavigationProps({
    navigator,
    title: "",
    category: "menu",
  });

  return Number(((navProps?.styles as any)?.top_border_width as number) ?? 0);
};

const TAB_BAR_HEIGHT_IOS = 49;
const TAB_BAR_HEIGHT_ANDROID = 56;

const useGetTabBarHeight = () =>
  platformSelect({
    ios: TAB_BAR_HEIGHT_IOS + (useSafeAreaInsets().bottom ?? 0),
    android: TAB_BAR_HEIGHT_ANDROID,
  }) as number;

export const useGetBottomTabBarHeight = (): number => {
  const topBorderWidth = useGetNavBarTopBorderWidth();
  const tabBarHeight = useGetTabBarHeight();

  const isBottomBarNavigation = useNavigationType() === MenuTypes.bottomTabBar;

  return !isBottomBarNavigation ? 0 : tabBarHeight + topBorderWidth;
};

// If current screen is active/focused (visible to the user)
export const useIsScreenActive = () => {
  const pathname = usePathname();
  const { currentRoute, videoModalState } = useNavigation();

  if (
    videoModalState.visible &&
    ["FULLSCREEN", "MAXIMIZED", "PIP"].includes(videoModalState.mode)
  ) {
    return false;
  }

  return pathname === currentRoute;
};

type Return = (prefix: string) => string;

export const useUniqueRouteSuffix = (): Return => {
  const pathname = usePathname();
  const { currentRoute } = useNavigation();

  const suffix = `currentRoute:${currentRoute}_pathname:${pathname}`;

  const withUniqueRouteSuffix = React.useCallback((prefix: string) => {
    if (isWeb()) {
      // we should use extended route prefix only on Web, where we have stack-navigator
      return `${prefix}___${suffix}`;
    }

    // for other platforms use previous version
    return prefix;
  }, []);

  return withUniqueRouteSuffix;
};

export const useNavbarId = () => {
  const withUniqueRouteSuffix = useUniqueRouteSuffix();

  return withUniqueRouteSuffix(QUICK_BRICK_NAVBAR);
};

export const useContentId = () => {
  const withUniqueRouteSuffix = useUniqueRouteSuffix();

  return withUniqueRouteSuffix(QUICK_BRICK_CONTENT);
};
