import {
  Meta,
  ProfileMetadata,
  SignedEventContent,
  User,
  UserRole
} from '../../../types'
import {
  Box,
  IconButton,
  List,
  ListItem,
  ListSubheader,
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableRow,
  Tooltip,
  Typography,
  useTheme
} from '@mui/material'
import {
  Download,
  CheckCircle,
  Cancel,
  HourglassTop
} from '@mui/icons-material'
import saveAs from 'file-saver'
import { kinds, Event } from 'nostr-tools'
import { useState, useEffect } from 'react'
import { toast } from 'react-toastify'
import { UserAvatar } from '../../../components/UserAvatar'
import { MetadataController } from '../../../controllers'
import { npubToHex, hexToNpub, parseJson } from '../../../utils'
import styles from '../style.module.scss'
import { SigitFile } from '../../../utils/file'

type DisplayMetaProps = {
  meta: Meta
  files: { [fileName: string]: SigitFile }
  submittedBy: string
  signers: `npub1${string}`[]
  viewers: `npub1${string}`[]
  creatorFileHashes: { [key: string]: string }
  currentFileHashes: { [key: string]: string | null }
  signedBy: `npub1${string}`[]
  nextSigner?: string
  getPrevSignersSig: (usersNpub: string) => string | null
}

export const DisplayMeta = ({
  meta,
  files,
  submittedBy,
  signers,
  viewers,
  creatorFileHashes,
  currentFileHashes,
  signedBy,
  nextSigner,
  getPrevSignersSig
}: DisplayMetaProps) => {
  const theme = useTheme()

  const textColor = theme.palette.getContrastText(
    theme.palette.background.paper
  )

  const [metadata, setMetadata] = useState<{ [key: string]: ProfileMetadata }>(
    {}
  )
  const [users, setUsers] = useState<User[]>([])

  useEffect(() => {
    signers.forEach((signer) => {
      const hexKey = npubToHex(signer)
      setUsers((prev) => {
        if (prev.findIndex((user) => user.pubkey === hexKey) !== -1) return prev

        return [
          ...prev,
          {
            pubkey: hexKey!,
            role: UserRole.signer
          }
        ]
      })
    })

    viewers.forEach((viewer) => {
      const hexKey = npubToHex(viewer)
      setUsers((prev) => {
        if (prev.findIndex((user) => user.pubkey === hexKey) !== -1) return prev

        return [
          ...prev,
          {
            pubkey: hexKey!,
            role: UserRole.viewer
          }
        ]
      })
    })
  }, [signers, viewers])

  useEffect(() => {
    const metadataController = MetadataController.getInstance()

    const hexKeys: string[] = [
      npubToHex(submittedBy)!,
      ...users.map((user) => user.pubkey)
    ]

    hexKeys.forEach((key) => {
      if (!(key in metadata)) {
        const handleMetadataEvent = (event: Event) => {
          const metadataContent =
            metadataController.extractProfileMetadataContent(event)

          if (metadataContent)
            setMetadata((prev) => ({
              ...prev,
              [key]: metadataContent
            }))
        }

        metadataController.on(key, (kind: number, event: Event) => {
          if (kind === kinds.Metadata) {
            handleMetadataEvent(event)
          }
        })

        metadataController
          .findMetadata(key)
          .then((metadataEvent) => {
            if (metadataEvent) handleMetadataEvent(metadataEvent)
          })
          .catch((err) => {
            console.error(`error occurred in finding metadata for: ${key}`, err)
          })
      }
    })
  }, [users, submittedBy, metadata])

  const downloadFile = async (fileName: string) => {
    const file = files[fileName]
    saveAs(file)
  }

  return (
    <List
      sx={{
        bgcolor: 'background.paper',
        marginTop: 2
      }}
      subheader={
        <ListSubheader className={styles.subHeader}>Meta Info</ListSubheader>
      }
    >
      <ListItem
        sx={{
          marginTop: 1,
          gap: '15px'
        }}
      >
        <Typography variant="h6" sx={{ color: textColor }}>
          Submitted By
        </Typography>
        <UserAvatar pubkey={submittedBy} isNameVisible={true} />
      </ListItem>
      <ListItem
        sx={{
          marginTop: 1,
          flexDirection: 'column',
          alignItems: 'flex-start'
        }}
      >
        <Typography variant="h6" sx={{ color: textColor }}>
          Files
        </Typography>
        <Box className={styles.filesWrapper}>
          {Object.entries(currentFileHashes).map(([filename, hash], index) => {
            const isValidHash = creatorFileHashes[filename] === hash

            return (
              <Box key={`file-${index}`} className={styles.file}>
                <Tooltip title="Download File" arrow>
                  <IconButton onClick={() => downloadFile(filename)}>
                    <Download />
                  </IconButton>
                </Tooltip>
                <Typography
                  component="label"
                  sx={{
                    color: textColor,
                    flexGrow: 1
                  }}
                >
                  {filename}
                </Typography>
                {isValidHash && (
                  <Tooltip title="File integrity check passed" arrow>
                    <CheckCircle sx={{ color: theme.palette.success.light }} />
                  </Tooltip>
                )}
                {!isValidHash && (
                  <Tooltip title="File integrity check failed" arrow>
                    <Cancel sx={{ color: theme.palette.error.main }} />
                  </Tooltip>
                )}
              </Box>
            )
          })}
        </Box>
      </ListItem>
      <ListItem sx={{ marginTop: 1 }}>
        <Table>
          <TableHead>
            <TableRow>
              <TableCell className={styles.tableCell}>User</TableCell>
              <TableCell className={styles.tableCell}>Role</TableCell>
              <TableCell>Signed Status</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {users.map((user) => (
              <DisplayUser
                key={user.pubkey}
                meta={meta}
                user={user}
                metadata={metadata}
                signedBy={signedBy}
                nextSigner={nextSigner}
                getPrevSignersSig={getPrevSignersSig}
              />
            ))}
          </TableBody>
        </Table>
      </ListItem>
    </List>
  )
}

enum PrevSignatureValidationEnum {
  Pending,
  Valid,
  Invalid
}

enum UserStatus {
  Viewer = 'Viewer',
  Awaiting = 'Awaiting Signature',
  Signed = 'Signed',
  Pending = 'Pending'
}

type DisplayUserProps = {
  meta: Meta
  user: User
  metadata: { [key: string]: ProfileMetadata }
  signedBy: `npub1${string}`[]
  nextSigner?: string
  getPrevSignersSig: (usersNpub: string) => string | null
}

const DisplayUser = ({
  meta,
  user,
  signedBy,
  nextSigner,
  getPrevSignersSig
}: DisplayUserProps) => {
  const theme = useTheme()

  const [userStatus, setUserStatus] = useState<UserStatus>(UserStatus.Pending)
  const [prevSignatureStatus, setPreviousSignatureStatus] =
    useState<PrevSignatureValidationEnum>(PrevSignatureValidationEnum.Pending)

  useEffect(() => {
    if (user.role === UserRole.viewer) {
      setUserStatus(UserStatus.Viewer)
      return
    }

    // check if user has signed the document
    const usersNpub = hexToNpub(user.pubkey)
    if (signedBy.includes(usersNpub)) {
      setUserStatus(UserStatus.Signed)
      return
    }

    // check if user is the next signer
    if (user.pubkey === nextSigner) {
      setUserStatus(UserStatus.Awaiting)
      return
    }
  }, [user, nextSigner, signedBy])

  useEffect(() => {
    const validatePrevSignature = async () => {
      const handleNullCase = () => {
        setPreviousSignatureStatus(PrevSignatureValidationEnum.Invalid)
        return
      }

      // get previous signers sig from the content of current signers signed event
      const npub = hexToNpub(user.pubkey)
      const signedEvent = await parseJson<Event>(
        meta.docSignatures[npub]
      ).catch((err) => {
        console.log(`err in parsing the singed event for ${npub}:>> `, err)
        toast.error(
          err.message ||
            'error occurred in parsing the signed event signature event'
        )
        return null
      })

      if (!signedEvent) return handleNullCase()

      // now that we have signed event of current signer, we'll extract prevSig from its content
      const parsedContent = await parseJson<SignedEventContent>(
        signedEvent.content
      ).catch((err) => {
        console.log(
          `an error occurred in parsing the content of signedEvent of ${npub}`,
          err
        )
        toast.error(
          err.message ||
            `an error occurred in parsing the content of signedEvent of ${npub}`
        )
        return null
      })

      if (!parsedContent) return handleNullCase()

      const prevSignersSignature = getPrevSignersSig(npub)

      if (!prevSignersSignature) return handleNullCase()

      setPreviousSignatureStatus(
        parsedContent.prevSig === prevSignersSignature
          ? PrevSignatureValidationEnum.Valid
          : PrevSignatureValidationEnum.Invalid
      )
    }

    if (userStatus === UserStatus.Signed) {
      validatePrevSignature()
    }
  }, [userStatus, meta.docSignatures, user.pubkey, getPrevSignersSig])

  return (
    <TableRow>
      <TableCell className={styles.tableCell}>
        <UserAvatar pubkey={user.pubkey} isNameVisible={true} />
      </TableCell>
      <TableCell className={styles.tableCell}>{user.role}</TableCell>
      <TableCell>
        <Box sx={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
          <Typography component="label">{userStatus}</Typography>
          {userStatus === UserStatus.Signed && (
            <>
              {prevSignatureStatus === PrevSignatureValidationEnum.Valid && (
                <Tooltip title="Contains valid signature of prev signer" arrow>
                  <CheckCircle sx={{ color: theme.palette.success.light }} />
                </Tooltip>
              )}
              {prevSignatureStatus === PrevSignatureValidationEnum.Invalid && (
                <Tooltip
                  title="Contains invalid signature of prev signer"
                  arrow
                >
                  <Cancel sx={{ color: theme.palette.error.main }} />
                </Tooltip>
              )}
            </>
          )}
          {userStatus === UserStatus.Awaiting && (
            <Tooltip title="Waiting for user's sign" arrow>
              <HourglassTop />
            </Tooltip>
          )}
        </Box>
      </TableCell>
    </TableRow>
  )
}
