import React, { useState, useRef, useEffect } from 'react'
import { Text, View, StatusBar, LogBox } from 'react-native'
import { AppearanceProvider } from 'react-native-appearance'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import AppLoading from 'expo-app-loading'
import * as Amplitude from 'expo-analytics-amplitude'
import * as Font from 'expo-font'
import { ArtistsScreen } from './src/screens/ArtistsScreen'
import { Navigation } from './src/Navigation'
import { useUser } from './src/user'
import { UserContext } from './src/contexts/UserContext'
import { LoginScreen } from './src/screens/LoginScreen'
import {
  NavigationContainerRef,
  NavigationContainer,
  NavigationState,
  PartialState,
} from '@react-navigation/native'
import { colorsV2, DarkTheme } from './src/Colors'
import { linking } from './src/linking'

import * as Sentry from 'sentry-expo'
import manifest from './src/appManifest'
import {
  LoginCarouselScreen,
  onBoardingContent,
} from './src/screens/LoginCarouselScreen'
import { useSetAmplitudeProperties } from './src/hooks/useSetAmplitudeProperties'
import { clearCache } from './src/image-cache'

import { UpdateBanner } from './src/components/UpdateBanner'
import { WhatsNewBanner } from './src/components/WhatsNewBanner'
import {
  useNotificationPermissions,
  useLogNotificationReceived,
  useLogNotificationReceivedInBackground,
} from './src/hooks/useNotifications'

import { TopErrorBoundary } from './src/components/ErrorBoundary'

// Freeze font size
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(Text as any).defaultProps = (Text as any).defaultProps || {}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(Text as any).defaultProps.allowFontScaling = false

LogBox.ignoreLogs(['Could not create sandbox iframe for pure fetch check'])

Sentry.init({
  dsn: manifest.extra.sentry.dsn,
  environment: manifest.extra.environment,
  // Toggle to true if you want to test Sentry integration locally. You can
  // filter events by "local" environment in Sentry.
  enableInExpoDevelopment: false,
})

// Gets the current screen from navigation state
const getActiveRoute = (
  state: NavigationState | PartialState<NavigationState>
  //TODO: Figure out correct return type
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
): any => {
  const route = state.routes[state.index]

  if (route.state) {
    // Dive into nested navigators
    return getActiveRoute(route.state)
  }

  return route
}

const getTimeAndReset = getTimer()

function getTimer() {
  let start = Date.now()
  return (): number => {
    const elapsed = Date.now() - start
    start = Date.now()
    return elapsed
  }
}

export const App: React.FC = () => {
  const user = useUser()
  useSetAmplitudeProperties(user)
  useLogNotificationReceived()
  useLogNotificationReceivedInBackground()
  useNotificationPermissions(user)

  const [assetLoadDone, setAssetLoadDone] = useState(false)
  const initApp = async () => {
    await Font.loadAsync({
      'Pilat Bold': require('./assets/fonts/Pilat/Pilat-Bold.otf'),
      'Pilat Regular': require('./assets/fonts/Pilat/Pilat-Regular.otf'),
    })
    await clearCache()
  }

  const hasOnboarded = user?.updateSeenVersion

  const routeNameRef = useRef<string>()
  const navigationRef = useRef<NavigationContainerRef>()

  useEffect(() => {
    const state = navigationRef?.current?.getRootState()
    if (state) {
      routeNameRef.current = getActiveRoute(state).name // Save the initial route name
    }
  }, [])

  const saveArtistId = (state: NavigationState | undefined) => {
    const currentRoute = getActiveRoute(state)

    //Remember "current" artist id based on last accessed route
    if (currentRoute?.params?.artistId) {
      user.setLastUsedArtistId(currentRoute?.params?.artistId)
    }
  }

  const sendAnalytics = (state: NavigationState | undefined) => {
    const previousRouteName = routeNameRef.current
    const currentRoute = getActiveRoute(state)
    const currentRouteName = currentRoute.name

    if (previousRouteName === currentRouteName) return

    const timeOnPreviousScreen = previousRouteName
      ? Math.round(getTimeAndReset() / 1000)
      : null

    const properties = {
      subject: 'user',
      verb: 'viewed',
      object: currentRouteName,
      event_source: 'navigation_route_changed',
      previous_event_name: 'enter_screen',
      screen: currentRouteName,
      previous_screen: previousRouteName,
      time_on_previous_screen: timeOnPreviousScreen,
      ...currentRoute?.params,
    }

    Amplitude.logEventWithPropertiesAsync(
      `${currentRouteName} viewed`,
      properties
    )

    // Save the current route name for later comparision
    routeNameRef.current = currentRouteName
  }

  return (
    <View style={{ flex: 1, backgroundColor: colorsV2.black }}>
      <TopErrorBoundary>
        {assetLoadDone ? (
          <View style={{ flex: 1 }}>
            <StatusBar hidden={true} />
            <UserContext.Provider value={user}>
              <UpdateBanner />
              <WhatsNewBanner />
              {user.accessToken ? (
                <AppearanceProvider>
                  <SafeAreaProvider>
                    {!hasOnboarded ? (
                      <LoginCarouselScreen
                        content={onBoardingContent}
                        onPressOk={user.setUpdateSeenVersion}
                      />
                    ) : user.lastUsedArtist ? (
                      <NavigationContainer
                        theme={DarkTheme}
                        ref={navigationRef}
                        onStateChange={(state) => {
                          sendAnalytics(state)
                          saveArtistId(state)
                        }}
                        linking={linking}
                      >
                        <Navigation />
                      </NavigationContainer>
                    ) : (
                      <ArtistsScreen user={user} />
                    )}
                  </SafeAreaProvider>
                </AppearanceProvider>
              ) : (
                <LoginScreen />
              )}
            </UserContext.Provider>
          </View>
        ) : (
          <AppLoading
            startAsync={initApp}
            onFinish={() => setAssetLoadDone(true)}
            onError={(e) => console.error(e)}
          ></AppLoading>
        )}
      </TopErrorBoundary>
    </View>
  )
}

export default App
