import { Event, kinds } from 'nostr-tools'
import { useEffect, useRef, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Outlet } from 'react-router-dom'
import { AppBar } from '../components/AppBar/AppBar'
import { LoadingSpinner } from '../components/LoadingSpinner'
import { MetadataController, NostrController } from '../controllers'
import {
  restoreState,
  setAuthState,
  setMetadataEvent,
  updateUserAppData
} from '../store/actions'
import { LoginMethods } from '../store/auth/types'
import { State } from '../store/rootReducer'
import { Dispatch } from '../store/store'
import { setUserRobotImage } from '../store/userRobotImage/action'
import {
  clearAuthToken,
  clearState,
  getRoboHashPicture,
  getUsersAppData,
  loadState,
  saveNsecBunkerDelegatedKey,
  subscribeForSigits
} from '../utils'
import { useAppSelector } from '../hooks'
import styles from './style.module.scss'
import { Footer } from '../components/Footer/Footer'

export const MainLayout = () => {
  const dispatch: Dispatch = useDispatch()
  const [isLoading, setIsLoading] = useState(true)
  const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState(`Loading App`)
  const authState = useSelector((state: State) => state.auth)
  const usersAppData = useAppSelector((state) => state.userAppData)

  // Ref to track if `subscribeForSigits` has been called
  const hasSubscribed = useRef(false)

  useEffect(() => {
    const metadataController = new MetadataController()

    const logout = () => {
      dispatch(
        setAuthState({
          keyPair: undefined,
          loggedIn: false,
          usersPubkey: undefined,
          loginMethod: undefined,
          nsecBunkerPubkey: undefined
        })
      )

      dispatch(setMetadataEvent(metadataController.getEmptyMetadataEvent()))

      // clear authToken saved in local storage
      clearAuthToken()
      clearState()

      // update nsecBunker delegated key
      const newDelegatedKey =
        NostrController.getInstance().generateDelegatedKey()
      saveNsecBunkerDelegatedKey(newDelegatedKey)
    }

    const restoredState = loadState()
    if (restoredState) {
      dispatch(restoreState(restoredState))

      const { loggedIn, loginMethod, usersPubkey, nsecBunkerRelays } =
        restoredState.auth

      if (loggedIn) {
        if (!loginMethod || !usersPubkey) return logout()

        if (loginMethod === LoginMethods.nsecBunker) {
          if (!nsecBunkerRelays) return logout()

          const nostrController = NostrController.getInstance()
          nostrController.nsecBunkerInit(nsecBunkerRelays).then(() => {
            nostrController.createNsecBunkerSigner(usersPubkey)
          })
        }

        const handleMetadataEvent = (event: Event) => {
          dispatch(setMetadataEvent(event))
        }

        metadataController.on(usersPubkey, (kind: number, event: Event) => {
          if (kind === kinds.Metadata) {
            handleMetadataEvent(event)
          }
        })

        metadataController.findMetadata(usersPubkey).then((metadataEvent) => {
          if (metadataEvent) handleMetadataEvent(metadataEvent)
        })
      } else {
        setIsLoading(false)
      }
    } else {
      setIsLoading(false)
    }
  }, [dispatch])

  useEffect(() => {
    if (authState.loggedIn && usersAppData) {
      const pubkey = authState.usersPubkey || authState.keyPair?.public

      if (pubkey && !hasSubscribed.current) {
        // Call `subscribeForSigits` only if it hasn't been called before
        subscribeForSigits(pubkey)

        // Mark `subscribeForSigits` as called
        hasSubscribed.current = true
      }
    }
  }, [authState, usersAppData])

  /**
   * When authState change user logged in / or app reloaded
   * we set robohash avatar in the global state based on user npub
   * so that avatar will be consistent across the app when kind 0 is empty
   */
  useEffect(() => {
    if (authState && authState.loggedIn) {
      const pubkey = authState.usersPubkey || authState.keyPair?.public

      if (pubkey) {
        dispatch(setUserRobotImage(getRoboHashPicture(pubkey)))
      }

      setIsLoading(true)
      setLoadingSpinnerDesc(`Loading SIGit history...`)
      getUsersAppData()
        .then((appData) => {
          if (appData) {
            dispatch(updateUserAppData(appData))
          }
        })
        .finally(() => setIsLoading(false))
    }
  }, [authState, dispatch])

  if (isLoading) return <LoadingSpinner desc={loadingSpinnerDesc} />

  return (
    <>
      <AppBar />
      <main className={styles.main}>
        <Outlet />
      </main>
      <Footer />
    </>
  )
}
