    import {
      RacingSansOne_400Regular
    } from '@expo-google-fonts/racing-sans-one';
    import { NavigationContainer } from '@react-navigation/native';
    import axios, { AxiosRequestConfig } from 'axios';
    import { useFonts } from 'expo-font';
    import { getItemAsync } from 'expo-secure-store';
    import { observer } from 'mobx-react';
    import React, { useEffect, useRef, useState } from 'react';
    import { AppState } from 'react-native';
    import { SafeAreaProvider } from 'react-native-safe-area-context';
    import { QueryClient, QueryClientProvider } from 'react-query';
    import { Splash } from '../components/Splash';
    import { API_URL, reportAxiosError } from '../globals';
    import { useAuth } from '../hooks/useAuth';
    import UnauthorizedNavigator from '../navigation/UnauthorizedNavigator';
    import AuthorizedNavigator from '../navigation/AuthorizedNavigator';
    import { authStore } from '../stores/AuthStore';

    export enum TokenState {
      CheckingToken,
      ValidToken,
      InvalidToken
    }

    const queryClient = new QueryClient();

    /**
     * Sub-root component of the app. Contains all global providers (NavigationContainer and SafeAreaProvider for React Navigation, QueryClientProvider for react-query) and is responsible for restricting unauthenticated users to the Intro screen by listening to {@link AuthStore}'s accessToken value.
     * @component
     */
    const Atlas : React.FC = () => {
      /**
       * Ref that keeps track of the app's state (opened or closed)
       */
      const appState = useRef(AppState.currentState);
      /**
       * Flag that is switched on when the app is checking for tokens in the keystore and in memory. When true, "Logging you in.." and a spinner will be displayed to the user.
       */
      const [checkingToken, setCheckingToken] = useState<boolean>(true);
      const { refreshAccessToken } = useAuth();
      const [fontsLoaded, error] = useFonts({
        RacingSansOne_400Regular
      });

      /**
       * Checks if there is an access token available in {@link AuthStore}, then checks if that access token is valid by calling the API.
       * If the response is valid, the access token will be stored in memory, otherwise the user will be directed to intro screen.
       */
      const checkToken = async () => {
        // check both the mobx store and secure storage for the token
        let currentAccessToken = authStore.accessToken;
        if (!currentAccessToken) {
          currentAccessToken = await getItemAsync('accessToken');
        }

        if (currentAccessToken) {
          // check to see if the token is valid by making test call
          const requestConfig: AxiosRequestConfig = {
            method: 'GET',
            url: API_URL + "/api/me/",
            headers: { "Authorization": "Bearer " + currentAccessToken }
          };

          try {
            await axios(requestConfig);
            await authStore.setAccessTokenAsync(currentAccessToken);
          } catch (error) {
            // check if access token can be refreshed
            if (error.response.status == 401) {
              try {
                await refreshAccessToken();

                // update authorization header w/ new token
                await axios({...requestConfig, headers: { "Authorization": "Bearer " + authStore.accessToken }});
              } catch (error) {
                await authStore.setAccessTokenAsync(null);
              }
            }
            // something went wrong with the api call, log error and delete access token
            reportAxiosError('Something went wrong when retrieving an access token', error)
            await authStore.setAccessTokenAsync(null);
          }
        }
        else {
          // no access token was found, user will be taken to login
          await authStore.setAccessTokenAsync(null);
        }

        setCheckingToken(false);
      }
      useEffect(() => {
        /**
         * useEffect hook that is responsible for registering an appState "change" handler that will call {@linkcode checkToken} each time the app is opened or closed on the device.
         * @memberOf Atlas
         */
        function registerAppStateChangeHandler() {
          AppState.addEventListener("change", (appState: string) => {
            if (appState == 'active') {
              checkToken();
            }
          });
          return () => {
            AppState.removeEventListener("change", (appState: string) => {
              if (appState == 'active') {
                checkToken();
              }
            });
          };
        }
        registerAppStateChangeHandler();
      }, []);

      useEffect(() => {
        /**
       * Calls {@linkcode checkToken} when a change to the access token stored in {@link AuthStore} is detected.
       * @memberOf Atlas
       */
        const checkTokenOnAccessTokenChange = () => {
          checkToken()
        }
        checkTokenOnAccessTokenChange()
      }, [authStore.accessToken]);

      return (
        <SafeAreaProvider>
          <NavigationContainer>
            {checkingToken ? <Splash/> :
            <QueryClientProvider client={queryClient}>
              {authStore.accessToken ? <AuthorizedNavigator  /> : <UnauthorizedNavigator /> }
            </QueryClientProvider> }
          </NavigationContainer>
        </SafeAreaProvider>
      );
    }

    export default observer(Atlas);