import 'react-native-gesture-handler'
import NetInfo from '@react-native-community/netinfo'
import client from '@root/api/client'
import Index from '@root/Index'
import { persistor, store } from '@root/store'
import { resetLocal } from '@root/utils/slices/instancesSlice'
import ThemeManager from '@utils/styles/ThemeManager'
import chalk from 'chalk'
import * as Analytics from 'expo-firebase-analytics'
import * as SplashScreen from 'expo-splash-screen'
import React, { useCallback, useEffect, useState } from 'react'
import { enableScreens } from 'react-native-screens'
import { onlineManager, QueryClient, QueryClientProvider } from 'react-query'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import * as Sentry from 'sentry-expo'

const ctx = new chalk.Instance({ level: 3 })
const startingLog = (type: 'log' | 'warn' | 'error', message: string) => {
  switch (type) {
    case 'log':
      console.log(ctx.bgBlue.bold(' Start up ') + ' ' + message)
      break
    case 'warn':
      console.log(ctx.bgBlue.bold(' Start up ') + ' ' + message)
      break
    case 'error':
      console.log(ctx.bgBlue.bold(' Start up ') + ' ' + message)
      break
  }
}

if (__DEV__) {
  Analytics.setDebugModeEnabled(true)

  startingLog('log', 'initializing wdyr')
  const whyDidYouRender = require('@welldone-software/why-did-you-render')
  whyDidYouRender(React, {
    trackHooks: true,
    hotReloadBufferMs: 1000
  })
}

startingLog('log', 'initializing Sentry')
Sentry.init({
  dsn:
    'https://c9e29aa05f774aca8f36def98244ce04@o389581.ingest.sentry.io/5571975',
  enableInExpoDevelopment: false,
  debug: __DEV__
})

startingLog('log', 'initializing react-query')
const queryClient = new QueryClient()

startingLog('log', 'initializing native screen')
enableScreens()

const App: React.FC = () => {
  startingLog('log', 'rendering App')
  const [appLoaded, setAppLoaded] = useState(false)
  const [localCorrupt, setLocalCorrupt] = useState(false)

  useEffect(() => {
    const onlineState = onlineManager.setEventListener(setOnline => {
      startingLog('log', 'added onlineManager listener')
      return NetInfo.addEventListener(state => {
        startingLog('warn', `setting online state ${state.isConnected}`)
        // @ts-ignore
        setOnline(state.isConnected)
      })
    })
    return () => {
      onlineState
    }
  }, [])

  useEffect(() => {
    const delaySplash = async () => {
      startingLog('log', 'delay splash')
      try {
        await SplashScreen.preventAutoHideAsync()
      } catch (e) {
        console.warn(e)
      }
    }
    delaySplash()
  }, [])
  useEffect(() => {
    const hideSplash = async () => {
      startingLog('log', 'hide splash')
      try {
        await SplashScreen.hideAsync()
      } catch (e) {
        console.warn(e)
      }
    }
    if (appLoaded) {
      hideSplash()
    }
  }, [appLoaded])

  const onBeforeLift = useCallback(() => {
    NetInfo.fetch().then(netInfo => {
      startingLog('log', 'on before lift')
      const localUrl = store.getState().instances.local.url
      const localToken = store.getState().instances.local.token
      if (netInfo.isConnected) {
        startingLog('log', 'network connected')
        if (localUrl && localToken) {
          startingLog('log', 'checking locally stored credentials')
          client({
            method: 'get',
            instance: 'remote',
            instanceDomain: localUrl,
            url: `accounts/verify_credentials`,
            headers: { Authorization: `Bearer ${localToken}` }
          })
            .then(res => {
              startingLog('log', 'local credential check passed')
              if (res.body.id !== store.getState().instances.local.account.id) {
                store.dispatch(resetLocal())
                setLocalCorrupt(true)
              }
              setAppLoaded(true)
            })
            .catch(error => {
              startingLog('error', 'local credential check failed')
              if (error.status && typeof error.status === 'number') {
                store.dispatch(resetLocal())
                setLocalCorrupt(true)
              }
              setAppLoaded(true)
            })
        } else {
          startingLog('log', 'no local credential found')
          setAppLoaded(true)
        }
      } else {
        startingLog('warn', 'network not connected')
        setAppLoaded(true)
      }
    })
  }, [])

  const main = useCallback(
    bootstrapped => {
      startingLog('log', 'bootstrapped')
      if (bootstrapped && appLoaded) {
        startingLog('log', 'loading actual app :)')
        require('@root/i18n/i18n')
        return (
          <ThemeManager>
            <Index localCorrupt={localCorrupt} />
          </ThemeManager>
        )
      } else {
        return null
      }
    },
    [appLoaded]
  )

  return (
    <QueryClientProvider client={queryClient}>
      <Provider store={store}>
        <PersistGate
          persistor={persistor}
          onBeforeLift={onBeforeLift}
          children={main}
        />
      </Provider>
    </QueryClientProvider>
  )
}

export default App
