import ContentCopyIcon from '@mui/icons-material/ContentCopy'
import {
  CircularProgress,
  List,
  ListItem,
  ListSubheader,
  TextField,
  Typography,
  useTheme
} from '@mui/material'
import { UnsignedEvent, nip19, kinds, VerifiedEvent } from 'nostr-tools'
import { useEffect, useMemo, useState } from 'react'
import { Link, useParams } from 'react-router-dom'
import { toast } from 'react-toastify'
import placeholderAvatar from '../../assets/images/nostr-logo.jpg'
import { MetadataController, NostrController } from '../../controllers'
import { NostrJoiningBlock, ProfileMetadata } from '../../types'
import styles from './style.module.scss'
import { useDispatch, useSelector } from 'react-redux'
import { State } from '../../store/rootReducer'
import { LoadingButton } from '@mui/lab'
import { Dispatch } from '../../store/store'
import { setMetadataEvent } from '../../store/actions'
import { LoadingSpinner } from '../../components/LoadingSpinner'
import { LoginMethods } from '../../store/auth/types'
import { ApiController } from '../../controllers/ApiController'
import { PostMediaResponse } from '../../types/media'

export const ProfilePage = () => {
  const theme = useTheme()

  const { npub } = useParams()

  const dispatch: Dispatch = useDispatch()

  const metadataController = useMemo(() => new MetadataController(), [])
  const nostrController = NostrController.getInstance()

  const [pubkey, setPubkey] = useState<string>()
  const [nostrJoiningBlock, setNostrJoiningBlock] =
    useState<NostrJoiningBlock | null>(null)
  const [profileMetadata, setProfileMetadata] = useState<ProfileMetadata>()
  const [savingProfileMetadata, setSavingProfileMetadata] = useState(false)
  const [avatarLoading, setAvatarLoading] = useState(false)
  const metadataState = useSelector((state: State) => state.metadata)
  const keys = useSelector((state: State) => state.auth?.keyPair)
  const { usersPubkey, loginMethod } = useSelector((state: State) => state.auth)
  const [uploadingImage, setUploadingImage] = useState<boolean>()

  const [isUsersOwnProfile, setIsUsersOwnProfile] = useState(false)

  const [isLoading, setIsLoading] = useState(true)
  const [loadingSpinnerDesc] = useState('Fetching metadata')

  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 (pubkey) {
      metadataController
        .getNostrJoiningBlockNumber(pubkey)
        .then((res) => {
          setNostrJoiningBlock(res)
        })
        .catch((err) => {
          // todo: handle error
          console.log('err :>> ', err)
        })
    }

    if (isUsersOwnProfile && metadataState) {
      const metadataContent = metadataController.extractProfileMetadataContent(
        metadataState as VerifiedEvent
      )
      if (metadataContent) {
        if (!metadataContent.lud16 || metadataContent.lud16.length < 1) {
          metadataContent.lud16 = getLud16()
        }
        
        setProfileMetadata(metadataContent)
        setIsLoading(false)
      }

      return
    }

    if (pubkey) {
      const getMetadata = async (pubkey: string) => {
        const metadataEvent = await metadataController
          .findMetadata(pubkey)
          .catch((err) => {
            toast.error(err)
            return null
          })

        if (metadataEvent) {
          const metadataContent =
            metadataController.extractProfileMetadataContent(metadataEvent)
          if (metadataContent) {
            if (!metadataContent.lud16 || metadataContent.lud16.length < 1) {
              metadataContent.lud16 = getLud16()
            }

            setProfileMetadata(metadataContent)
          }
        }

        setIsLoading(false)
      }

      getMetadata(pubkey)
    }
  }, [isUsersOwnProfile, metadataState, pubkey, metadataController])

  const getLud16 = () => {
    let lud16 = ''

    if (npub) {
      if (npub) lud16 = `${npub}@npub.cash`
    }

    return lud16
  }  

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

          setProfileMetadata((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 () => {
    setSavingProfileMetadata(true)

    const content = JSON.stringify(profileMetadata)

    // We need to omit cachedAt and create new event
    // Relay will reject if created_at is too late
    const updatedMetadataState: UnsignedEvent = {
      content: content,
      created_at: Math.round(Date.now() / 1000),
      kind: kinds.Metadata,
      pubkey: pubkey!,
      tags: metadataState?.tags || []
    }

    const signedEvent = await nostrController
      .signEvent(updatedMetadataState)
      .catch((error) => {
        toast.error(`Error saving profile metadata. ${error}`)
      })

    if (signedEvent) {
      if (!metadataController.validate(signedEvent)) {
        toast.error(`Metadata is not valid.`)
      }

      await metadataController.publishMetadataEvent(signedEvent)

      dispatch(setMetadataEvent(signedEvent))
    }

    setSavingProfileMetadata(false)
  }

  const onProfileImageChange = (event: any) => {
    const file = event?.target?.files[0]

    if (file) {
      setUploadingImage(true)

      const apiController = new ApiController()

      apiController.uploadMediaImage(file).then((imageResponse: PostMediaResponse) => {
        if (imageResponse && imageResponse.directLink) {
          setTimeout(() => {
            setProfileMetadata((prev) => ({
              ...prev,
              picture: imageResponse.directLink
            }))
          }, 200)
        } else {
          toast.error(`Uploading image failed. Please try again.`)
        }
      })
      .catch((err) => {
        console.error('Error uploading image to media server.', err)
      })
      .finally(() => {
        setUploadingImage(false)
      })
    }
  }

  const progressSpinner = (show = false) => {
    if (!show) return undefined

    return <CircularProgress size={20} />
  }

  const generateRobotAvatar = () => {
    setAvatarLoading(true)

    const robotAvatarLink = `https://robohash.org/${npub}.png?set=set3`

    setProfileMetadata((prev) => ({
      ...prev,
      picture: ''
    }))

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

  return (
    <>
      {isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
      <div className={styles.container}>
        <List
          sx={{
            bgcolor: 'background.paper',
            marginTop: 2
          }}
          subheader={
            <ListSubheader
              sx={{
                paddingBottom: 1,
                paddingTop: 1,
                fontSize: '1.5rem'
              }}
              className={styles.subHeader}
            >
              Profile Settings
            </ListSubheader>
          }
        >
          {profileMetadata && (
            <div>
              <ListItem
                sx={{
                  marginTop: 1,
                  display: 'flex',
                  flexDirection: 'column'
                }}
              >
                <img
                  onError={(event: any) => {
                    event.target.src = placeholderAvatar
                  }}
                  onLoad={() => {
                    setAvatarLoading(false)
                  }}
                  className={styles.img}
                  src={profileMetadata.picture || placeholderAvatar}
                  alt='Profile Image'
                />

                <LoadingButton onClick={generateRobotAvatar} loading={avatarLoading}>Generate Robot Avatar</LoadingButton>

                <TextField
                  onChange={onProfileImageChange}
                  type="file"
                  size="small"
                  label="Profile Image"
                  className={styles.textField}
                  InputProps={{
                    endAdornment: progressSpinner(uploadingImage)
                  }}
                  focused
                />

                {nostrJoiningBlock && (
                  <Typography
                    sx={{
                      color: theme.palette.getContrastText(
                        theme.palette.background.paper
                      )
                    }}
                    component={Link}
                    to={`https://njump.me/${nostrJoiningBlock.encodedEventPointer}`}
                    target='_blank'
                  >
                    On nostr since {nostrJoiningBlock.block.toLocaleString()}
                  </Typography>
                )}
              </ListItem>

              {editItem('name', 'Username')}
              {editItem('display_name', '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 === LoginMethods.privateKey &&
                    keys &&
                    keys.private &&
                    copyItem(
                      '••••••••••••••••••••••••••••••••••••••••••••••••••',
                      'Private Key',
                      keys.private
                    )}
                </>
              )}
            </div>
          )}
        </List>
        {isUsersOwnProfile && (
          <LoadingButton
            loading={savingProfileMetadata}
            variant='contained'
            onClick={handleSaveMetadata}
          >
            SAVE
          </LoadingButton>
        )}
      </div>
    </>
  )
}
