import { ReactNode, createContext, useEffect } from 'react'
import { User } from 'common/user'
import { onIdTokenChanged } from 'firebase/auth'
import {
  auth,
  listenForUser,
  getUser,
  setCachedReferralInfoForUser,
} from 'web/lib/firebase/users'
import { deleteTokenCookies, setTokenCookies } from 'web/lib/firebase/auth'
import { createUser } from 'web/lib/firebase/api'
import { randomString } from 'common/util/random'
import { identifyUser, setUserProperty } from 'web/lib/service/analytics'
import { useStateCheckEquality } from 'web/hooks/use-state-check-equality'

// Either we haven't looked up the logged in user yet (undefined), or we know
// the user is not logged in (null), or we know the user is logged in (User).
type AuthUser = undefined | null | User

const CACHED_USER_KEY = 'CACHED_USER_KEY'

const ensureDeviceToken = () => {
  let deviceToken = localStorage.getItem('device-token')
  if (!deviceToken) {
    deviceToken = randomString()
    localStorage.setItem('device-token', deviceToken)
  }
  return deviceToken
}

export const AuthContext = createContext<AuthUser>(undefined)

export function AuthProvider(props: {
  children: ReactNode
  serverUser?: AuthUser
}) {
  const { children, serverUser } = props
  const [authUser, setAuthUser] = useStateCheckEquality<AuthUser>(serverUser)
  useEffect(() => {
    if (serverUser === undefined) {
      const cachedUser = localStorage.getItem(CACHED_USER_KEY)
      setAuthUser(cachedUser && JSON.parse(cachedUser))
    }
  }, [setAuthUser, serverUser])

  useEffect(() => {
    return onIdTokenChanged(auth, async (fbUser) => {
      if (fbUser) {
        setTokenCookies({
          id: await fbUser.getIdToken(),
          refresh: fbUser.refreshToken,
        })
        let user = await getUser(fbUser.uid)
        if (!user) {
          const deviceToken = ensureDeviceToken()
          user = (await createUser({ deviceToken })).user as User
        }
        setAuthUser(user)
        // Persist to local storage, to reduce login blink next time.
        // Note: Cap on localStorage size is ~5mb
        localStorage.setItem(CACHED_USER_KEY, JSON.stringify(user))
        setCachedReferralInfoForUser(user)
      } else {
        // User logged out; reset to null
        deleteTokenCookies()
        setAuthUser(null)
        localStorage.removeItem(CACHED_USER_KEY)
      }
    })
  }, [setAuthUser])

  const authUserId = authUser?.id
  const authUsername = authUser?.username
  useEffect(() => {
    if (authUserId && authUsername) {
      identifyUser(authUserId)
      setUserProperty('username', authUsername)
      return listenForUser(authUserId, setAuthUser)
    }
  }, [authUserId, authUsername, setAuthUser])

  return (
    <AuthContext.Provider value={authUser}>{children}</AuthContext.Provider>
  )
}
