import { EventTemplate } from 'nostr-tools'
import { MetadataController, NostrController } from '.'
import { setAuthState, setMetadataEvent } from '../store/actions'
import store from '../store/store'
import {
  base64DecodeAuthToken,
  base64EncodeSignedEvent,
  getAuthToken,
  getRoboHashPicture,
  getVisitedLink,
  saveAuthToken
} from '../utils'
import { appPrivateRoutes } from '../routes'
import { SignedEvent } from '../types'

export class AuthController {
  private nostrController: NostrController
  private metadataController: MetadataController

  constructor() {
    this.nostrController = NostrController.getInstance()
    this.metadataController = new MetadataController()
  }

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

        const emptyMetadata = this.metadataController.getEmptyMetadataEvent()

        emptyMetadata.content = JSON.stringify({
          picture: getRoboHashPicture(pubkey)
        })

        store.dispatch(setMetadataEvent(emptyMetadata))
      })

    // Nostr uses unix timestamps
    const timestamp = Math.floor(Date.now() / 1000)
    const { hostname } = window.location

    const authEvent: EventTemplate = {
      kind: 1,
      tags: [],
      content: `${hostname}-${timestamp}`,
      created_at: timestamp
    }

    const signedAuthEvent = await this.nostrController.signEvent(authEvent)
    this.createAndSaveAuthToken(signedAuthEvent)

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

    const visitedLink = getVisitedLink()

    if (visitedLink) {
      const { pathname, search } = visitedLink

      return Promise.resolve(`${pathname}${search}`)
    } else {
      // Navigate user in
      return Promise.resolve(appPrivateRoutes.homePage)
    }
  }

  checkSession() {
    const savedAuthToken = getAuthToken()

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

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

      return
    }

    store.dispatch(
      setAuthState({
        loggedIn: false,
        usersPubkey: undefined
      })
    )
  }

  private createAndSaveAuthToken(signedAuthEvent: SignedEvent) {
    const base64Encoded = base64EncodeSignedEvent(signedAuthEvent)

    // save newly created auth token (base64 nostr singed event) in local storage along with expiry time
    saveAuthToken(base64Encoded)
    return base64Encoded
  }
}