import { useEffect, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'

import { Button, Divider, TextField } from '@mui/material'
import { toast } from 'react-toastify'

import { hexToBytes } from '@noble/hashes/utils'
import { launch as launchNostrLoginDialog } from 'nostr-login'
import { getPublicKey, nip19 } from 'nostr-tools'

import { LoadingSpinner } from '../../components/LoadingSpinner'
import { useAppDispatch, useAuth } from '../../hooks'
import { updateKeyPair, updateLoginMethod } from '../../store/actions'
import { LoginMethod } from '../../store/auth/types'
import { KeyboardCode } from '../../types'

import styles from './styles.module.scss'

export const Nostr = () => {
  const [searchParams] = useSearchParams()
  const { authAndGetMetadataAndRelaysMap } = useAuth()

  const dispatch = useAppDispatch()
  const navigate = useNavigate()

  const [isLoading, setIsLoading] = useState(false)
  const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
  const [inputValue, setInputValue] = useState('')

  const navigateAfterLogin = (path: string) => {
    const callbackPath = searchParams.get('callbackPath')

    if (callbackPath) {
      // base64 decoded path
      const path = atob(callbackPath)
      navigate(path)
      return
    }

    navigate(path)
  }

  const [isNostrExtensionAvailable, setIsNostrExtensionAvailable] =
    useState(false)

  useEffect(() => {
    setTimeout(() => {
      setIsNostrExtensionAvailable(!!window.nostr)
    }, 500)
  }, [])

  /**
   * Call login function when enter is pressed
   */
  const handleInputKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
    if (
      event.code === KeyboardCode.Enter ||
      event.code === KeyboardCode.NumpadEnter
    ) {
      event.preventDefault()
      login()
    }
  }

  /**
   * Login with NSEC or HEX private key
   * @param privateKey in HEX format
   */
  const loginWithNsec = async (privateKey?: Uint8Array) => {
    let nsec = ''

    if (privateKey) {
      nsec = nip19.nsecEncode(privateKey)
    } else {
      nsec = inputValue

      try {
        privateKey = nip19.decode(nsec).data as Uint8Array
      } catch (err) {
        toast.error(`Error decoding the nsec. ${err}`)
      }
    }

    if (!privateKey) {
      toast.error(
        'Snap, we failed to convert the private key you provided. Please make sure key is valid.'
      )
      setIsLoading(false)
      return
    }

    const publickey = getPublicKey(privateKey)

    dispatch(
      updateKeyPair({
        private: nsec,
        public: publickey
      })
    )
    dispatch(updateLoginMethod(LoginMethod.privateKey))

    setIsLoading(true)
    setLoadingSpinnerDesc('Authenticating and finding metadata')

    const redirectPath = await authAndGetMetadataAndRelaysMap(publickey).catch(
      (err) => {
        toast.error('Error occurred in authentication: ' + err)
        return null
      }
    )

    if (redirectPath) navigateAfterLogin(redirectPath)

    setIsLoading(false)
    setLoadingSpinnerDesc('')
  }

  const login = () => {
    if (inputValue.startsWith('nsec')) {
      return loginWithNsec()
    }

    // Check if maybe hex nsec
    try {
      const privateKey = hexToBytes(inputValue)
      const publickey = getPublicKey(privateKey)

      if (publickey) return loginWithNsec(privateKey)
    } catch (err) {
      console.warn('err', err)
    }

    toast.error('Invalid format, please use: private key (hex or nsec)')
    return
  }

  return (
    <>
      {isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
      {isNostrExtensionAvailable && (
        <>
          <label className={styles.label} htmlFor="extension-login">
            Login by using a{' '}
            <a
              rel="noopener"
              href="https://github.com/nostrband/nostr-login"
              target="_blank"
            >
              nostr-login
            </a>
          </label>
          <Button
            id="nostr-login"
            variant="contained"
            onClick={() => {
              launchNostrLoginDialog()
            }}
          >
            Nostr Login
          </Button>
          <Divider
            sx={{
              fontSize: '16px'
            }}
          >
            or
          </Divider>
        </>
      )}
      <form autoComplete="off">
        <TextField
          onKeyDown={handleInputKeyDown}
          label="Private key (Not recommended)"
          type="password"
          autoComplete="off"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          fullWidth
          margin="dense"
        />
      </form>
      <Button
        disabled={!inputValue}
        onClick={login}
        variant="contained"
        fullWidth
      >
        Login
      </Button>
    </>
  )
}
