import { EventTemplate } from 'nostr-tools'
import { useCallback } from 'react'
import { NostrController } from '../controllers'
import { appPrivateRoutes } from '../routes'
import {
  setAuthState,
  setRelayMapAction,
  setUserProfile
} from '../store/actions'
import {
  base64DecodeAuthToken,
  compareObjects,
  createAndSaveAuthToken,
  getAuthToken,
  getRelayMapFromNDKRelayList,
  unixNow
} from '../utils'
import { useAppDispatch, useAppSelector } from './store'
import { useNDKContext } from './useNDKContext'
import { useDvm } from './useDvm'
import { getFileServerMap } from '../utils/file-servers.ts'
import store from '../store/store.ts'
import { setServerMapAction } from '../store/servers/action.ts'

export const useAuth = () => {
  const dispatch = useAppDispatch()
  const { getRelayInfo } = useDvm()
  const { findMetadata, getNDKRelayList, fetchEvent } = useNDKContext()

  const authState = useAppSelector((state) => state.auth)
  const relaysState = useAppSelector((state) => state.relays)

  const checkSession = useCallback(() => {
    const savedAuthToken = getAuthToken()

    if (savedAuthToken) {
      const signedEvent = base64DecodeAuthToken(savedAuthToken)

      dispatch(
        setAuthState({
          loggedIn: true,
          usersPubkey: signedEvent.pubkey
        })
      )
      return
    }

    dispatch(
      setAuthState({
        loggedIn: false,
        usersPubkey: undefined
      })
    )
  }, [dispatch])

  /**
   * Function will authenticate user by signing an auth event
   * which is done by calling the sign() function, where appropriate
   * method will be chosen (extension or keys)
   *
   * @param pubkey of the user trying to login
   * @returns url to redirect if authentication successfull
   * or error if otherwise
   */
  const authAndGetMetadataAndRelaysMap = useCallback(
    async (pubkey: string) => {
      try {
        const profile = await findMetadata(pubkey)
        dispatch(setUserProfile(profile))
      } catch (err) {
        console.warn('Error occurred while finding metadata', err)
      }

      const timestamp = unixNow()
      const { href } = window.location

      const authEvent: EventTemplate = {
        kind: 27235,
        tags: [
          ['u', href],
          ['method', 'GET']
        ],
        content: '',
        created_at: timestamp
      }

      const nostrController = NostrController.getInstance()
      const signedAuthEvent = await nostrController.signEvent(authEvent)
      createAndSaveAuthToken(signedAuthEvent)

      dispatch(
        setAuthState({
          loggedIn: true,
          usersPubkey: pubkey
        })
      )

      const [ndkRelayList, serverMap] = await Promise.all([
        getNDKRelayList(pubkey),
        getFileServerMap(pubkey, fetchEvent)
      ])

      const relays = ndkRelayList.relays

      if (relays.length < 1) {
        // Navigate user to relays page if a relay map is empty
        return appPrivateRoutes.relays
      }

      if (Object.keys(serverMap).length < 1) {
        // Navigate user to servers page if a server map is empty
        return appPrivateRoutes.servers
      }

      getRelayInfo(relays)

      const relayMap = getRelayMapFromNDKRelayList(ndkRelayList)

      if (authState.loggedIn) {
        if (!compareObjects(relaysState?.map, relayMap))
          dispatch(setRelayMapAction(relayMap))
        if (!compareObjects(store.getState().servers?.map, serverMap.map))
          dispatch(setServerMapAction(serverMap.map))
      }

      return appPrivateRoutes.homePage
    },
    [
      dispatch,
      getNDKRelayList,
      fetchEvent,
      getRelayInfo,
      authState.loggedIn,
      findMetadata,
      relaysState?.map
    ]
  )

  return {
    authAndGetMetadataAndRelaysMap,
    checkSession
  }
}
