import * as Amplitude from 'expo-analytics-amplitude'
import { AppManifest } from 'expo-constants/build/Constants.types'
import { CodedError } from 'expo-modules-core'
import * as Updates from 'expo-updates'
import { useState, useRef, useEffect, useCallback } from 'react'
import { AppState, AppStateStatus } from 'react-native'

import * as SecureStore from '../secure-store'
import { useAsyncStorage } from './useAsyncStore'

/**
 * Check for updates on focus, then provide callbacks to restart the app or dismiss
 *
 * @returns newVersion    Are we ready with a new OTA bundle
 * @returns onAccept      Callback to run on user accept
 * @returns onDismiss     Callback to run on dismiss
 */
type UpdateState = {
  newVersion: boolean
  onAccept: () => void
  onDismiss: () => void
}
export function useShowUpdates(): UpdateState {
  const appState = useRef(AppState.currentState)
  const [updateBannerVersion, setUpdateBannerVersion] = useAsyncStorage<string>(
    SecureStore.Key.UPDATE_BANNER_VERSION
  )
  const [version, setVersion] = useState<string>('unknown')
  const [newVersion, setNewVersion] = useState(false)

  const checkForUpdateOnFocus = useCallback((nextAppState: AppStateStatus) => {
    if (appFocused(nextAppState)) fetchNewUpdate()
    appState.current = nextAppState
  }, [])

  useEffect(() => {
    AppState.addEventListener('change', checkForUpdateOnFocus)
    return () => AppState.removeEventListener('change', checkForUpdateOnFocus)
  }, [checkForUpdateOnFocus])

  const appFocused = (nextAppState: AppStateStatus) =>
    appState.current.match(/inactive|background/) && nextAppState === 'active'

  const fetchNewUpdate = () => {
    Updates.checkForUpdateAsync()
      .then(({ isAvailable }) => {
        if (!isAvailable) return

        Updates.fetchUpdateAsync().then((updateFetchResult) => {
          if (!updateFetchResult.isNew) return
          const manifest = updateFetchResult.manifest as AppManifest
          const version = manifest?.version ?? 'unknown'
          Amplitude.logEventWithPropertiesAsync('update_banner_shown', {
            version,
          })
          setVersion(version)
        })
      })
      .catch((reason) => {
        if (__DEV__) {
          const error = reason as CodedError
          // Updates are disabled in development, silence this error
          if (error.code === 'ERR_UPDATES_DISABLED') return
        }
        throw reason
      })
  }

  useEffect(() => {
    setNewVersion(!['unknown', updateBannerVersion].includes(version))
  }, [version, updateBannerVersion])

  const onDismiss = () => {
    Amplitude.logEventWithPropertiesAsync('update_dismissed', { version })
    setUpdateBannerVersion(version)
  }

  const onAccept = () => {
    Amplitude.logEventWithPropertiesAsync('update_accepted', { version })
    setUpdateBannerVersion(version)
    Updates.reloadAsync()
  }

  return { newVersion, onAccept, onDismiss }
}
