import {
  NavigationContainerRef,
  NavigationContainer,
  NavigationState,
  PartialState,
} from '@react-navigation/native'
import * as Amplitude from 'expo-analytics-amplitude'
import * as SplashScreen from 'expo-splash-screen'
import React, { useRef, useEffect } from 'react'
import { Text, View, StatusBar, LogBox } from 'react-native'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import * as Sentry from 'sentry-expo'

import { colors, DarkTheme } from './src/Colors'
import { LoginNavigation, Navigation } from './src/Navigation'
import manifest from './src/appManifest'
import { TopErrorBoundary } from './src/components/ErrorBoundary'
import { GraphQLSDK } from './src/components/Network'
import { Storybook } from './src/components/Storybook'
import { UpdateBanner } from './src/components/UpdateBanner'
import { WhatsNewBanner } from './src/components/WhatsNewBanner'
import { GraphQLContext } from './src/contexts/GraphQLContext'
import { UserContext } from './src/contexts/UserContext'
import {
  useNotificationPermissions,
  useLogNotificationReceived,
  useLogNotificationReceivedInBackground,
} from './src/hooks/useNotifications'
import { useScreenshotListener } from './src/hooks/useScreenshotListener'
import { useSetAmplitudeProperties } from './src/hooks/useSetAmplitudeProperties'
import { useSplashScreen } from './src/hooks/useSplashScreen'
import { linking } from './src/linking'
import { ArtistsScreen } from './src/screens/ArtistsScreen'
import {
  LoginCarouselScreen,
  onBoardingContent,
} from './src/screens/LoginCarouselScreen'
import { useUser } from './src/user'

// Freeze font size, inspired by: https://stackoverflow.com/a/65192671/274426
/* eslint-disable @typescript-eslint/no-explicit-any */
;(Text as any).defaultProps = (Text as any).defaultProps || {}
;(Text as any).defaultProps.allowFontScaling = false
/* eslint-enable @typescript-eslint/no-explicit-any */

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

Sentry.init({
  dsn: manifest.extra.sentry.dsn,
  environment: manifest.extra.environment,
  enableInExpoDevelopment: manifest.extra.environment !== 'local',
  debug: manifest.extra.environment === 'development',
})

// 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 => {
  if (state.index == null) {
    return undefined
  }

  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
  }
}

SplashScreen.preventAutoHideAsync()

export const App: React.FC = () => {
  const routeNameRef = useRef<string>('Library')
  const navigationRef = useRef<NavigationContainerRef>(null)

  const { isAppReady, onLayoutRootView } = useSplashScreen()
  const user = useUser()

  useSetAmplitudeProperties(user)
  useLogNotificationReceived()
  useLogNotificationReceivedInBackground()
  useNotificationPermissions(user)
  useScreenshotListener(routeNameRef)

  const hasOnboarded = user?.updateSeenVersion

  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 = state != null ? getActiveRoute(state) : undefined

    //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 = state != null ? getActiveRoute(state) : undefined
    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
  }

  const g = GraphQLSDK(user)

  if (!isAppReady) {
    return null
  }

  return (
    <View style={{ flex: 1, backgroundColor: colors.black }}>
      <TopErrorBoundary>
        <View style={{ flex: 1 }} onLayout={onLayoutRootView}>
          <StatusBar hidden />
          <UserContext.Provider value={user}>
            <GraphQLContext.Provider value={g}>
              <UpdateBanner />
              <WhatsNewBanner />
              {user.accessToken ? (
                <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>
              ) : (
                <NavigationContainer theme={DarkTheme}>
                  <LoginNavigation />
                </NavigationContainer>
              )}
            </GraphQLContext.Provider>
          </UserContext.Provider>
        </View>
      </TopErrorBoundary>
    </View>
  )
}

export default process.env.RTI_STORYBOOK === 'true' ? Storybook : App
