import { ReactElement, useState, useContext, useEffect } from 'react'
import { makeStyles, createStyles } from '@material-ui/core/styles'
import { useParams } from 'react-router-dom'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import { RefreshCw, ArrowDown, ExternalLink } from 'react-feather'
import CircularProgress from '@material-ui/core/CircularProgress'
import { Utils } from '@ethersphere/bee-js'
import { encodeManifestReference } from '@ethersphere/swarm-cid'

import Header from '../components/Header'
import Footer from '../components/Footer'
import Logo from '../components/Logo'
import { AssetPreview } from '../components/AssetPreview'
import Layout from '../components/Layout'
import FileNotFound from '../components/FileNotFound'
import UnknownFile from '../components/UnknownFile'
import LoadingFile from '../components/LoadingFile'
import InvalidSwarmHash from '../components/InvalidSwarmHash'
import { BZZ_LINK_DOMAIN } from '../constants'

import { Context } from '../providers/bee'

import text from '../translations'

const useStyles = makeStyles(() =>
  createStyles({
    button: {
      width: '100%',
      display: 'flex',
      justifyContent: 'space-between',
    },
  }),
)

const SharePage = (): ReactElement => {
  const classes = useStyles()

  const { hash } = useParams<{ hash: string }>()
  const bzzLink = `https://${encodeManifestReference(hash!)}.${BZZ_LINK_DOMAIN}/` //eslint-disable-line
  const { getMetadata, getChunk, download } = useContext(Context)
  const [entries, setEntries] = useState<Record<string, string>>({})
  const [metadata, setMetadata] = useState<Metadata | undefined>()
  const [preview, setPreview] = useState<string | undefined>()
  const [isLoading, setIsLoading] = useState<boolean>(true)
  const [isDownloading, setIsDownloading] = useState<boolean>(false)
  const [chunkExists, setChunkExists] = useState<boolean>(false)
  const [errorMsg, setErrorMsg] = useState<string | null>(null)

  useEffect(() => {
    if (!(Utils.isHexString(hash, 64) || Utils.isHexString(hash, 128))) {
      setErrorMsg('Not a valid Swarm Reference')
      setIsLoading(false)

      return
    }

    setErrorMsg(null)
    setEntries({})
    setIsLoading(true)
    getMetadata(hash)
      .then(({ metadata, preview, entries }) => {
        setMetadata(metadata)
        setPreview(preview)
        setEntries(entries)
        setIsLoading(false)
      })
      .catch(() => {
        // There are no metadata, but maybe there is a retrievable file
        getChunk(hash)
          .then(d => setChunkExists(Boolean(d.byteLength)))
          .catch(() => setChunkExists(false))
          .finally(() => setIsLoading(false))
      })
  }, [hash, getChunk, getMetadata])

  const handleDownload = () => {
    setIsDownloading(true)
    // The hash is already validated that is why there can be not a null assertion
    download(hash!, entries, metadata).finally(() => setIsDownloading(false)) //eslint-disable-line
  }

  if (isLoading) {
    return (
      <Layout
        top={[
          <Header key="top1">
            <Logo />
          </Header>,
        ]}
        center={[
          <div key="middle1">
            <LoadingFile />
          </div>,
        ]}
        bottom={[<div key="bottom1" />]}
      />
    )
  }

  // The hash is wrong, display error message
  if (errorMsg) {
    return (
      <Layout
        top={[
          <Header key="top1">
            <Logo />
          </Header>,
        ]}
        center={[<InvalidSwarmHash key="center" />]}
        bottom={[]}
      />
    )
  }

  // There are some metadata, display them and offer downloading the content
  if (Object.keys(entries).length > 0) {
    return (
      <Layout
        top={[
          <Header key="top1">
            <Logo />
          </Header>,
          <Typography key="top2" variant="subtitle1">
            {text.accessHashPage.useButtonToDownload}
          </Typography>,
        ]}
        center={[
          <div key="center1">
            <AssetPreview previewUri={preview} metadata={metadata} />
            {metadata?.isWebsite && metadata?.hash && (
              <Button variant="contained" className={classes.button} href={bzzLink} target="blank">
                <ExternalLink strokeWidth={1} />
                {text.accessHashPage.openWebsite}
                <ExternalLink style={{ opacity: 0 }} />
              </Button>
            )}
          </div>,
        ]}
        bottom={[
          <Footer key="bottom1">
            <Button
              variant="contained"
              className={classes.button}
              size="large"
              onClick={handleDownload}
              disabled={isDownloading}
            >
              {isDownloading ? <CircularProgress size={24} color="inherit" /> : <ArrowDown strokeWidth={1} />}
              {isDownloading ? text.accessHashPage.downloadingAction : text.accessHashPage.downloadAction}
              <ArrowDown style={{ opacity: 0 }} />
            </Button>
          </Footer>,
        ]}
      />
    )
  }

  // We have no metadata, but there is a chunk at that address. Allow users to download.
  if (chunkExists) {
    return (
      <Layout
        top={[
          <Header key="top1">
            <Logo />
          </Header>,
          <Typography key="top2" variant="subtitle1">
            {text.accessHashPage.useButtonToDownload}
          </Typography>,
        ]}
        center={[<UnknownFile key="center1" />]}
        bottom={[
          <Footer key="bottom1">
            <Button
              variant="contained"
              className={classes.button}
              size="large"
              onClick={handleDownload}
              disabled={isDownloading}
            >
              {isDownloading ? <CircularProgress size={24} color="inherit" /> : <ArrowDown strokeWidth={1} />}
              {isDownloading ? text.accessHashPage.downloadingAction : text.accessHashPage.downloadAction}
              <ArrowDown style={{ opacity: 0 }} />
            </Button>
          </Footer>,
        ]}
      />
    )
  }

  // We could not retrieve anything, display generic message
  return (
    <Layout
      top={[
        <Header key="top1">
          <Logo />
        </Header>,
        <Typography key="top2" variant="subtitle1">
          {text.accessHashPage.tagline}
        </Typography>,
      ]}
      center={[<FileNotFound key="center" />]}
      bottom={[
        <Footer key="bottom1">
          <Button variant="contained" className={classes.button} size="large" onClick={() => window.location.reload()}>
            <RefreshCw />
            {text.accessHashPage.retryAction}
            <RefreshCw style={{ opacity: 0 }} />
          </Button>
        </Footer>,
      ]}
    />
  )
}

export default SharePage