import styles from './style.module.scss'
import { Container } from '../../../components/Container'
import { Footer } from '../../../components/Footer/Footer.tsx'
import {
  Box,
  Button,
  CircularProgress,
  InputAdornment,
  List,
  ListItem,
  ListItemText,
  TextField
} from '@mui/material'
import StorageIcon from '@mui/icons-material/Storage'
import DeleteIcon from '@mui/icons-material/Delete'
import { useState } from 'react'
import { toast } from 'react-toastify'
import { FileServerMap, KeyboardCode } from '../../../types'
import {
  getFileServerMap,
  publishFileServer
} from '../../../utils/file-servers.ts'
import { useAppSelector, useNDKContext } from '../../../hooks'
import { useDidMount } from '../../../hooks'
import { isValidUrl, MAXIMUM_BLOSSOMS_LENGTH } from '../../../utils'
import axios from 'axios'
import { cloneDeep } from 'lodash'

const protocol = 'https://'

const errors = {
  urlNotValid:
    'New server URL is not valid. Example of valid server URL: blossom.sigit.io'
}

export const ServersPage = () => {
  const usersPubkey = useAppSelector((state) => state.auth?.usersPubkey)

  const [newServerURL, setNewServerURL] = useState<string>('')
  const [newRelayURLerror, setNewRelayURLerror] = useState<string>()
  const [loadingServers, setLoadingServers] = useState<boolean>(true)

  const [blossomServersMap, setBlossomServersMap] = useState<FileServerMap>({})

  const { ndk, fetchEvent, publish } = useNDKContext()

  useDidMount(() => {
    fetchFileServers()
  })

  const fetchFileServers = async () => {
    if (usersPubkey) {
      const servers = await getFileServerMap(usersPubkey, fetchEvent)

      if (servers.map) {
        if (Object.keys(servers.map).length === 0) {
          serverRequirementWarning()
        }

        setBlossomServersMap(servers.map)
      }
    } else {
      noUserKeyWarning()
    }

    setLoadingServers(false)
  }

  const noUserKeyWarning = () => toast.warning('No user key available.')

  const serverRequirementWarning = () =>
    toast.warning('At least one Blossom server is needed for SIGit to work.')

  const handleAddNewServer = async () => {
    if (!newServerURL.length) {
      setNewRelayURLerror(errors.urlNotValid)
      return
    }

    if (Object.keys(blossomServersMap).length === MAXIMUM_BLOSSOMS_LENGTH) {
      return toast.warning(
        `You can only add a maximum of ${MAXIMUM_BLOSSOMS_LENGTH} blossom servers.`
      )
    }

    const serverURL = `${protocol}${newServerURL?.trim().replace(protocol, '')}`
    if (!serverURL) return

    // Check if new server is a valid URL
    if (!isValidUrl(serverURL)) {
      if (serverURL !== protocol) {
        setNewRelayURLerror(errors.urlNotValid)
        return
      }
    }

    if (Object.keys(blossomServersMap).includes(serverURL))
      return toast.warning('This server is already added.')

    const valid = await validateFileServer(serverURL)
    if (!valid)
      return toast.warning(
        `Server URL ${serverURL} does not seem to be a valid file server.`
      )

    setNewRelayURLerror('')
    const tempBlossomServersMap = blossomServersMap
    tempBlossomServersMap[serverURL] = { write: true, read: true }
    setBlossomServersMap(tempBlossomServersMap)
    setNewServerURL('')

    publishFileServersList(tempBlossomServersMap)
  }

  const handleDeleteServer = (serverURL: string) => {
    if (Object.keys(blossomServersMap).length === 1)
      return serverRequirementWarning()

    // Remove server from the list
    const tempBlossomServersMap = cloneDeep(blossomServersMap)
    delete tempBlossomServersMap[serverURL]

    setBlossomServersMap(tempBlossomServersMap)
    // Publish new list to the relays
    publishFileServersList(tempBlossomServersMap)
  }

  const publishFileServersList = (fileServersMap: FileServerMap) => {
    if (!usersPubkey)
      return toast.warning(
        'No user key available, please reload and try again.'
      )

    publishFileServer(fileServersMap, usersPubkey, ndk, publish)
      .then((res) => {
        toast.success(res)
      })
      .catch((err) => {
        toast.error(err)
      })
  }

  const handleInputKeydown = (event: React.KeyboardEvent<HTMLDivElement>) => {
    if (
      event.code === KeyboardCode.Enter ||
      event.code === KeyboardCode.NumpadEnter
    ) {
      event.preventDefault()
      handleAddNewServer()
    }
  }

  /**
   * Checks if the file server is up and valid
   * For now check will just include sending a GET request and checking if
   * returned HTML includes word `Blossom`.
   *
   * Probably later, there will be appropriate sepc universal to all file servers
   * which would include some kind of "check" endpoint.
   * @param serverURL
   */
  const validateFileServer = (serverURL: string) => {
    return new Promise((resolve) => {
      axios
        .get(serverURL)
        .then((res) => {
          if (res && res.data?.toLowerCase().includes('blossom server')) {
            resolve(true)
          } else {
            resolve(false)
          }
        })
        .catch((err) => {
          console.error('Error validating file server.', err)
          resolve(false)
        })
    })
  }

  return (
    <Container className={`settings-container ${styles.container}`}>
      <Box className={styles.serverAddContainer}>
        <TextField
          label="Add new blossom server"
          value={newServerURL}
          onKeyDown={handleInputKeydown}
          onChange={(e) => setNewServerURL(e.target.value)}
          helperText={newRelayURLerror}
          error={!!newRelayURLerror}
          InputProps={{
            startAdornment: (
              <InputAdornment position="start">{protocol}</InputAdornment>
            )
          }}
          className={styles.serverURItextfield}
        />
        <Button variant="contained" onClick={() => handleAddNewServer()}>
          Add
        </Button>
      </Box>
      <Box className={styles.sectionTitle}>
        <StorageIcon className={styles.sectionIcon} />
        <span>YOUR BLOSSOM SERVERS</span>
      </Box>

      {loadingServers && (
        <div className="text-center">
          <CircularProgress />
        </div>
      )}

      {blossomServersMap && (
        <Box className={styles.serversContainer}>
          {Object.keys(blossomServersMap).map((key) => (
            <ServerItem
              key={key}
              serverURL={key}
              preventDelete={Object.keys(blossomServersMap).length === 1}
              handleDeleteServer={handleDeleteServer}
            />
          ))}
        </Box>
      )}
      <Footer />
    </Container>
  )
}

interface ServerItemProps {
  serverURL: string
  preventDelete?: boolean
  handleDeleteServer?: (serverURL: string) => void
}

const ServerItem = ({
  serverURL,
  handleDeleteServer,
  preventDelete
}: ServerItemProps) => {
  return (
    <Box className={styles.server}>
      <List>
        <ListItem>
          <span
            className={[
              styles.connectionStatus,
              styles.connectionStatusConnected
            ].join(' ')}
          />

          <ListItemText primary={serverURL} />

          <Box
            onClick={() => handleDeleteServer && handleDeleteServer(serverURL)}
            className={`${styles.leaveServerContainer} ${preventDelete ? styles.disabled : ''}`}
          >
            <DeleteIcon />
            <span>Remove</span>
          </Box>
        </ListItem>
      </List>
    </Box>
  )
}
