import {
  createNavigatorFactory,
  createScreenFactory,
  type NavigatorTypeBagBase,
  type ParamListBase,
  StackActions,
  type StaticConfig,
  type TabActionHelpers,
  type TabNavigationState,
  TabRouter,
  type TabRouterOptions,
  type TypedNavigator,
  useNavigationBuilder,
} from '@react-navigation/native';
import * as React from 'react';

import { NativeBottomTabView } from './NativeBottomTabView.native';
import type {
  NativeBottomTabNavigationEventMap,
  NativeBottomTabNavigationOptions,
  NativeBottomTabNavigationProp,
  NativeBottomTabNavigatorProps,
} from './types';

function NativeBottomTabNavigator({
  id,
  initialRouteName,
  backBehavior,
  children,
  layout,
  screenListeners,
  screenOptions,
  screenLayout,
  UNSTABLE_router,
  UNSTABLE_routeNamesChangeBehavior,
  ...rest
}: NativeBottomTabNavigatorProps) {
  const { state, navigation, descriptors, render } = useNavigationBuilder<
    TabNavigationState<ParamListBase>,
    TabRouterOptions,
    TabActionHelpers<ParamListBase>,
    NativeBottomTabNavigationOptions,
    NativeBottomTabNavigationEventMap
  >(TabRouter, {
    id,
    initialRouteName,
    backBehavior,
    children,
    layout,
    screenListeners,
    screenOptions,
    screenLayout,
    UNSTABLE_router,
    UNSTABLE_routeNamesChangeBehavior,
  });

  const focusedRouteKey = state.routes[state.index].key;
  const previousRouteKeyRef = React.useRef(focusedRouteKey);

  React.useEffect(() => {
    const previousRouteKey = previousRouteKeyRef.current;

    if (
      previousRouteKey !== focusedRouteKey &&
      descriptors[previousRouteKey]?.options.popToTopOnBlur
    ) {
      const currentState = navigation.getState();
      const prevRoute = currentState.routes.find(
        (route) => route.key === previousRouteKey
      );

      if (
        prevRoute?.state?.type === 'stack' &&
        prevRoute.state.key &&
        (prevRoute.state.index ?? prevRoute.state.routes.length - 1) > 0
      ) {
        navigation.dispatch({
          ...StackActions.popToTop(),
          target: prevRoute.state.key,
        });
      }
    }

    previousRouteKeyRef.current = focusedRouteKey;
  }, [descriptors, focusedRouteKey, navigation]);

  return render(
    <NativeBottomTabView
      {...rest}
      state={state}
      navigation={navigation}
      descriptors={descriptors}
    />
  );
}

export type NativeBottomTabTypeBag<
  ParamList extends ParamListBase = ParamListBase,
  NavigatorID extends string | undefined = string | undefined,
> = {
  ParamList: ParamList;
  NavigatorID: NavigatorID;
  State: TabNavigationState<ParamList>;
  ScreenOptions: NativeBottomTabNavigationOptions;
  EventMap: NativeBottomTabNavigationEventMap;
  NavigationList: {
    [RouteName in keyof ParamList]: NativeBottomTabNavigationProp<
      ParamList,
      RouteName,
      NavigatorID
    >;
  };
  Navigator: typeof NativeBottomTabNavigator;
};

export function createNativeBottomTabNavigator<
  const ParamList extends ParamListBase,
  const NavigatorID extends string | undefined = string | undefined,
  const TypeBag extends NavigatorTypeBagBase = NativeBottomTabTypeBag<
    ParamList,
    NavigatorID
  >,
  const Config extends StaticConfig<TypeBag> = StaticConfig<TypeBag>,
>(config?: Config): TypedNavigator<TypeBag, Config> {
  return createNavigatorFactory(NativeBottomTabNavigator)(config);
}

export const createNativeBottomTabScreen =
  createScreenFactory<NativeBottomTabTypeBag>();
