import { Button, TextField } from '@mui/material'
import JSZip from 'jszip'
import { useCallback, useEffect, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { toast } from 'react-toastify'
import { useAppSelector } from '../../hooks'
import { appPrivateRoutes, appPublicRoutes } from '../../routes'
import { Meta } from '../../types'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSearch } from '@fortawesome/free-solid-svg-icons'
import { Select } from '../../components/Select'
import { DisplaySigit } from '../../components/DisplaySigit'
import { useDropzone } from 'react-dropzone'
import { Container } from '../../components/Container'
import styles from './style.module.scss'
import {
  extractSigitCardDisplayInfo,
  SigitCardDisplayInfo,
  SigitStatus
} from '../../utils'
import { Footer } from '../../components/Footer/Footer'

// Unsupported Filter options are commented
const FILTERS = [
  'Show all',
  // 'Drafts',
  'In-progress',
  'Completed'
  // 'Archived'
] as const
type Filter = (typeof FILTERS)[number]

const SORT_BY = [
  {
    label: 'Newest',
    value: 'desc'
  },
  { label: 'Oldest', value: 'asc' }
] as const
type Sort = (typeof SORT_BY)[number]['value']

export const HomePage = () => {
  const navigate = useNavigate()
  const [searchParams, setSearchParams] = useSearchParams()
  const q = searchParams.get('q') ?? ''

  useEffect(() => {
    const searchInput = document.getElementById('q') as HTMLInputElement | null
    if (searchInput) {
      searchInput.value = q
    }
  }, [q])

  const [sigits, setSigits] = useState<{ [key: string]: Meta }>({})
  const [parsedSigits, setParsedSigits] = useState<{
    [key: string]: SigitCardDisplayInfo
  }>({})
  const usersAppData = useAppSelector((state) => state.userAppData)

  useEffect(() => {
    if (usersAppData) {
      const getSigitInfo = async () => {
        const parsedSigits: { [key: string]: SigitCardDisplayInfo } = {}
        for (const key in usersAppData.sigits) {
          if (Object.prototype.hasOwnProperty.call(usersAppData.sigits, key)) {
            const sigitInfo = await extractSigitCardDisplayInfo(
              usersAppData.sigits[key]
            )
            if (sigitInfo) {
              parsedSigits[key] = sigitInfo
            }
          }
        }

        setParsedSigits({
          ...parsedSigits
        })
      }

      setSigits(usersAppData.sigits)
      getSigitInfo()
    }
  }, [usersAppData])

  const onDrop = useCallback(
    async (acceptedFiles: File[]) => {
      // When uploading single file check if it's .sigit.zip
      if (acceptedFiles.length === 1) {
        const file = acceptedFiles[0]

        // Check if the file extension is .sigit.zip
        const fileName = file.name
        const fileExtension = fileName.slice(-10) // ".sigit.zip" has 10 characters
        if (fileExtension === '.sigit.zip') {
          const zip = await JSZip.loadAsync(file).catch((err) => {
            console.log('err in loading zip file :>> ', err)
            toast.error(err.message || 'An error occurred in loading zip file.')
            return null
          })

          if (!zip) return

          // navigate to sign page if zip contains keys.json
          if ('keys.json' in zip.files) {
            return navigate(appPrivateRoutes.sign, {
              state: { uploadedZip: file }
            })
          }

          // navigate to verify page if zip contains meta.json
          if ('meta.json' in zip.files) {
            return navigate(appPublicRoutes.verify, {
              state: { uploadedZip: file }
            })
          }

          toast.error('Invalid SiGit zip file')
          return
        }
      }

      // navigate to create page
      navigate(appPrivateRoutes.create, {
        state: { uploadedFiles: acceptedFiles }
      })
    },
    [navigate]
  )

  const { getRootProps, getInputProps, isDragActive, open } = useDropzone({
    onDrop,
    noClick: true
  })

  const [filter, setFilter] = useState<Filter>('Show all')
  const [sort, setSort] = useState<Sort>('desc')

  return (
    <div {...getRootProps()} tabIndex={-1}>
      <Container className={styles.container}>
        <div className={styles.header}>
          <div className={styles.filters}>
            <Select
              name={'filter-select'}
              value={filter}
              setValue={setFilter}
              options={FILTERS.map((f) => {
                return {
                  label: f,
                  value: f
                }
              })}
            />
            <Select
              name={'sort-select'}
              value={sort}
              setValue={setSort}
              options={SORT_BY.map((s) => {
                return { ...s }
              })}
            />
          </div>
          <div className={styles.actionButtons}>
            <form
              className={styles.search}
              onSubmit={(e) => {
                e.preventDefault()
                const searchInput = e.currentTarget.elements.namedItem(
                  'q'
                ) as HTMLInputElement
                searchParams.set('q', searchInput.value)
                setSearchParams(searchParams)
              }}
            >
              <TextField
                id="q"
                name="q"
                placeholder="Search"
                size="small"
                type="search"
                defaultValue={q}
                onChange={(e) => {
                  // Handle the case when users click native search input's clear or x
                  if (e.currentTarget.value === '') {
                    searchParams.delete('q')
                    setSearchParams(searchParams)
                  }
                }}
                sx={{
                  width: '100%',
                  fontSize: '16px',
                  borderTopLeftRadius: 'var(----mui-shape-borderRadius)',
                  borderBottomLeftRadius: 'var(----mui-shape-borderRadius)',
                  '& .MuiInputBase-root': {
                    borderTopRightRadius: 0,
                    borderBottomRightRadius: 0
                  },
                  '& .MuiInputBase-input': {
                    padding: '7px 14px'
                  },
                  '& .MuiOutlinedInput-notchedOutline': {
                    display: 'none'
                  }
                }}
              />
              <Button
                type="submit"
                sx={{
                  minWidth: '44px',
                  padding: '11.5px 12px',
                  borderTopLeftRadius: 0,
                  borderBottomLeftRadius: 0
                }}
                variant={'contained'}
                aria-label="submit search"
              >
                <FontAwesomeIcon icon={faSearch} />
              </Button>
            </form>
          </div>
        </div>
        <button
          className={`${styles.dropzone} ${isDragActive ? styles.isDragActive : ''}`}
          tabIndex={0}
          onClick={open}
          type="button"
          aria-label="upload files"
        >
          <input id="file-upload" {...getInputProps()} />
          {isDragActive ? (
            <label htmlFor="file-upload">Drop the files here ...</label>
          ) : (
            <label htmlFor="file-upload">Click or drag files to upload!</label>
          )}
        </button>
        <div className={styles.submissions}>
          {Object.keys(parsedSigits)
            .filter((s) => {
              const { title, signedStatus } = parsedSigits[s]
              const isMatch = title?.toLowerCase().includes(q.toLowerCase())
              switch (filter) {
                case 'Completed':
                  return signedStatus === SigitStatus.Complete && isMatch
                case 'In-progress':
                  return signedStatus === SigitStatus.Partial && isMatch
                case 'Show all':
                  return isMatch
                default:
                  console.error('Filter case not handled.')
              }
            })
            .sort((a, b) => {
              const x = parsedSigits[a].createdAt ?? 0
              const y = parsedSigits[b].createdAt ?? 0
              return sort === 'desc' ? y - x : x - y
            })
            .map((key) => (
              <DisplaySigit
                key={`sigit-${key}`}
                parsedMeta={parsedSigits[key]}
                meta={sigits[key]}
              />
            ))}
        </div>
      </Container>
      <Footer />
    </div>
  )
}
