import React, { useEffect, useRef, useState } from 'react'
import { useParams } from 'react-router-dom'
import { toast } from 'react-toastify'

import { SmartToy } from '@mui/icons-material'
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import LaunchIcon from '@mui/icons-material/Launch'
import { LoadingButton } from '@mui/lab'
import {
  Box,
  IconButton,
  InputProps,
  List,
  ListItem,
  ListSubheader,
  TextField,
  Tooltip
} from '@mui/material'

import { NDKEvent, NDKUserProfile, serializeProfile } from '@nostr-dev-kit/ndk'
import { launch as launchNostrLoginDialog } from 'nostr-login'
import { kinds, nip19, UnsignedEvent } from 'nostr-tools'

import { NostrController } from '../../../controllers'

import { useNDKContext } from '../../../hooks'
import { useAppDispatch, useAppSelector } from '../../../hooks/store'

import { getRoboHashPicture, unixNow } from '../../../utils'

import { Container } from '../../../components/Container'
import { Footer } from '../../../components/Footer/Footer'
import { LoadingSpinner } from '../../../components/LoadingSpinner'

import { setUserProfile as updateUserProfile } from '../../../store/actions'
import { LoginMethod, NostrLoginAuthMethod } from '../../../store/auth/types'
import { Dispatch } from '../../../store/store'

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

export const ProfileSettingsPage = () => {
  const dispatch: Dispatch = useAppDispatch()

  const { npub } = useParams()
  const { ndk, findMetadata, publish } = useNDKContext()

  const [pubkey, setPubkey] = useState<string>()
  const [userProfile, setUserProfile] = useState<NDKUserProfile | null>(null)

  const userRobotImage = useAppSelector((state) => state.user.robotImage)
  const currentUserProfile = useAppSelector((state) => state.user.profile)
  const keys = useAppSelector((state) => state.auth?.keyPair)
  const { usersPubkey, loginMethod, nostrLoginAuthMethod } = useAppSelector(
    (state) => state.auth
  )

  const [savingProfileMetadata, setSavingProfileMetadata] = useState(false)
  const [isUsersOwnProfile, setIsUsersOwnProfile] = useState(false)
  const [isLoading, setIsLoading] = useState(true)
  const [loadingSpinnerDesc] = useState('Fetching metadata')

  const robotSet = useRef(1)

  useEffect(() => {
    if (npub) {
      try {
        const hexPubkey = nip19.decode(npub).data as string
        setPubkey(hexPubkey)

        if (hexPubkey === usersPubkey) setIsUsersOwnProfile(true)
      } catch (error) {
        toast.error('Error occurred in decoding npub' + error)
      }
    }
  }, [npub, usersPubkey])

  useEffect(() => {
    if (isUsersOwnProfile && currentUserProfile) {
      setUserProfile(currentUserProfile)

      setIsLoading(false)

      return
    }

    if (pubkey) {
      findMetadata(pubkey)
        .then((profile) => {
          setUserProfile(profile)
        })
        .catch((err) => {
          toast.error(err)
        })
        .finally(() => {
          setIsLoading(false)
        })
    }
  }, [ndk, isUsersOwnProfile, currentUserProfile, pubkey, findMetadata])

  const editItem = (
    key: keyof NDKUserProfile,
    label: string,
    multiline = false,
    rows = 1,
    inputProps?: InputProps
  ) => (
    <ListItem sx={{ marginTop: 1 }}>
      <TextField
        label={label}
        id={label.split(' ').join('-')}
        value={userProfile![key] || ''}
        size="small"
        multiline={multiline}
        rows={rows}
        className={styles.textField}
        disabled={!isUsersOwnProfile}
        InputProps={inputProps}
        onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
          const { value } = event.target

          setUserProfile((prev) => ({
            ...prev,
            [key]: value
          }))
        }}
      />
    </ListItem>
  )

  const copyItem = (
    value: string,
    label: string,
    copyValue?: string,
    isPassword = false
  ) => (
    <ListItem
      sx={{ marginTop: 1 }}
      onClick={() => {
        navigator.clipboard.writeText(copyValue || value)

        toast.success('Copied to clipboard', {
          autoClose: 1000,
          hideProgressBar: true
        })
      }}
    >
      <TextField
        label={label}
        id={label.split(' ').join('-')}
        defaultValue={value}
        size="small"
        className={styles.textField}
        disabled
        type={isPassword ? 'password' : 'text'}
        InputProps={{
          endAdornment: <ContentCopyIcon className={styles.copyItem} />
        }}
      />
    </ListItem>
  )

  const handleSaveMetadata = async () => {
    if (!userProfile) return

    setSavingProfileMetadata(true)

    const serializedProfile = serializeProfile(userProfile)

    const unsignedEvent: UnsignedEvent = {
      content: serializedProfile,
      created_at: unixNow(),
      kind: kinds.Metadata,
      pubkey: pubkey!,
      tags: []
    }

    const nostrController = NostrController.getInstance()
    const signedEvent = await nostrController
      .signEvent(unsignedEvent)
      .catch((error) => {
        toast.error(`Error saving profile metadata. ${error}`)
        return null
      })

    if (!signedEvent) {
      setSavingProfileMetadata(false)
      return
    }

    const ndkEvent = new NDKEvent(ndk, signedEvent)
    const publishedOnRelays = await publish(ndkEvent)

    // Handle cases where publishing failed or succeeded
    if (publishedOnRelays.length === 0) {
      toast.error('Failed to publish event on any relay')
    } else {
      toast.success(
        `Event published successfully to the following relays\n\n${publishedOnRelays.join(
          '\n'
        )}`
      )

      dispatch(updateUserProfile(userProfile))
    }

    setSavingProfileMetadata(false)
  }

  /**
   * Called by clicking on the robot icon inside Picture URL input
   * On every click, next robohash set will be generated.
   * There are 5 sets at the moment, after 5th set function will start over from set 1.
   */
  const generateRobotAvatar = () => {
    robotSet.current++
    if (robotSet.current > 5) robotSet.current = 1

    const robotAvatarLink = getRoboHashPicture(npub!, robotSet.current)

    setUserProfile((prev) => ({
      ...prev,
      picture: robotAvatarLink
    }))
  }

  /**
   *
   * @returns robohash generate button, loading spinner or no button
   */
  const robohashButton = () => {
    return (
      <Tooltip title="Generate a robohash avatar">
        <IconButton onClick={generateRobotAvatar}>
          <SmartToy />
        </IconButton>
      </Tooltip>
    )
  }

  /**
   * Handles the logic for Image URL.
   * If no picture in kind 0 found - use robohash avatar
   *
   * @returns robohash image url
   */
  const getProfileImage = (profile: NDKUserProfile) => {
    if (!isUsersOwnProfile) {
      return profile.image || getRoboHashPicture(npub!)
    }

    // userRobotImage is used only when visiting own profile
    // while kind 0 picture is not set
    return profile.image || userRobotImage || getRoboHashPicture(npub!)
  }

  return (
    <>
      {isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
      <Container className={styles.container}>
        <List
          sx={{
            bgcolor: 'background.paper',
            marginTop: 2
          }}
          subheader={
            <ListSubheader
              sx={{
                paddingBottom: 1,
                paddingTop: 1,
                fontSize: '1.5rem',
                zIndex: 2
              }}
              className={styles.subHeader}
            >
              Profile Settings
            </ListSubheader>
          }
        >
          {userProfile && (
            <div>
              <ListItem
                sx={{
                  marginTop: 1,
                  display: 'flex',
                  flexDirection: 'column'
                }}
              >
                {userProfile.banner ? (
                  <img
                    className={styles.bannerImg}
                    src={userProfile.banner}
                    alt="Banner Image"
                  />
                ) : (
                  <Box className={styles.noBanner}> No banner found </Box>
                )}
              </ListItem>

              {editItem('banner', 'Banner URL', undefined, undefined)}

              <ListItem
                sx={{
                  marginTop: 1,
                  display: 'flex',
                  flexDirection: 'column'
                }}
              >
                <img
                  onError={(event: React.SyntheticEvent<HTMLImageElement>) => {
                    event.currentTarget.src = getRoboHashPicture(npub!)
                  }}
                  className={styles.img}
                  src={getProfileImage(userProfile)}
                  alt="Profile Image"
                />
              </ListItem>

              {editItem('image', 'Picture URL', undefined, undefined, {
                endAdornment: isUsersOwnProfile ? robohashButton() : undefined
              })}

              {editItem('name', 'Username')}
              {editItem('displayName', 'Display Name')}
              {editItem('nip05', 'Nostr Address (nip05)')}
              {editItem('lud16', 'Lightning Address (lud16)')}
              {editItem('about', 'About', true, 4)}
              {editItem('website', 'Website')}
              {isUsersOwnProfile && (
                <>
                  {usersPubkey &&
                    copyItem(nip19.npubEncode(usersPubkey), 'Public Key')}

                  {loginMethod === LoginMethod.privateKey &&
                    keys &&
                    keys.private &&
                    copyItem(
                      '••••••••••••••••••••••••••••••••••••••••••••••••••',
                      'Private Key',
                      keys.private
                    )}
                </>
              )}
              {isUsersOwnProfile && (
                <>
                  {loginMethod === LoginMethod.nostrLogin &&
                    nostrLoginAuthMethod === NostrLoginAuthMethod.Local && (
                      <ListItem
                        sx={{ marginTop: 1 }}
                        onClick={() => {
                          launchNostrLoginDialog('import')
                        }}
                      >
                        <TextField
                          label="Private Key (nostr-login)"
                          defaultValue="••••••••••••••••••••••••••••••••••••••••••••••••••"
                          size="small"
                          className={styles.textField}
                          disabled
                          type={'password'}
                          InputProps={{
                            endAdornment: (
                              <LaunchIcon className={styles.copyItem} />
                            )
                          }}
                        />
                      </ListItem>
                    )}
                </>
              )}
            </div>
          )}
        </List>
        {isUsersOwnProfile && (
          <LoadingButton
            loading={savingProfileMetadata}
            variant="contained"
            onClick={handleSaveMetadata}
          >
            SAVE
          </LoadingButton>
        )}
      </Container>
      <Footer />
    </>
  )
}
