import {
  ChangeEvent,
  KeyboardEvent,
  useCallback,
  useEffect,
  useState,
} from 'react'
import { getJSON } from '../../../../infrastructure/fetch-json'
import useAbortController from '../../../../shared/hooks/use-abort-controller'

const LOCAL_AND_DOMAIN_REGEX = /([^@]+)@(.+)/

function matchLocalAndDomain(emailHint: string) {
  const match = emailHint.match(LOCAL_AND_DOMAIN_REGEX)
  if (match) {
    return { local: match[1], domain: match[2] }
  } else {
    return { local: null, domain: null }
  }
}

type InstitutionInfo = { hostname: string; university: { id: number } }

let domainCache = new Map<string, InstitutionInfo>()

export function clearDomainCache() {
  domainCache = new Map<string, InstitutionInfo>()
}

type AddEmailInputProps = {
  onChange: (value: string, institution?: InstitutionInfo) => void
}

export function AddEmailInput({ onChange }: AddEmailInputProps) {
  const { signal } = useAbortController()

  const [suggestion, setSuggestion] = useState<string>(null)
  const [inputValue, setInputValue] = useState<string>(null)
  const [matchedInstitution, setMatchedInstitution] =
    useState<InstitutionInfo>(null)

  useEffect(() => {
    if (inputValue == null) {
      return
    }
    if (matchedInstitution && suggestion === inputValue) {
      onChange(inputValue, matchedInstitution)
    } else {
      onChange(inputValue)
    }
  }, [onChange, inputValue, suggestion, matchedInstitution])

  const handleEmailChange = useCallback(
    (event: ChangeEvent<HTMLInputElement>) => {
      const hint = event.target.value
      setInputValue(hint)
      const match = matchLocalAndDomain(hint)
      if (!matchedInstitution?.hostname.startsWith(match.domain)) {
        setSuggestion(null)
      }
      if (!match.domain) {
        return
      }
      if (domainCache.has(match.domain)) {
        const cachedDomain = domainCache.get(match.domain)
        setSuggestion(`${match.local}@${cachedDomain.hostname}`)
        setMatchedInstitution(cachedDomain)
        return
      }
      const query = `?hostname=${match.domain}&limit=1`
      getJSON(`/institutions/domains${query}`, { signal })
        .then(data => {
          if (!(data && data[0])) {
            return
          }
          const hostname = data[0]?.hostname
          if (hostname) {
            domainCache.set(match.domain, data[0])
            setSuggestion(`${match.local}@${hostname}`)
            setMatchedInstitution(data[0])
          } else {
            setSuggestion(null)
            setMatchedInstitution(null)
          }
        })
        .catch(error => {
          setSuggestion(null)
          setMatchedInstitution(null)
          console.error(error)
        })
    },
    [signal, matchedInstitution]
  )

  const handleKeyDownEvent = useCallback(
    (event: KeyboardEvent) => {
      if (event.key === 'Tab' || event.key === 'Enter') {
        event.preventDefault()
        if (suggestion) {
          setInputValue(suggestion)
        }
      }
    },
    [suggestion]
  )

  return (
    <div className="input-suggestions">
      <div className="form-control input-suggestions-shadow">
        <div className="input-suggestions-shadow-suggested">
          {suggestion || ''}
        </div>
      </div>

      <input
        id="affiliations-email"
        className="form-control input-suggestions-main"
        type="email"
        onChange={handleEmailChange}
        onKeyDown={handleKeyDownEvent}
        value={inputValue || ''}
        placeholder="e.g. johndoe@mit.edu"
      />
    </div>
  )
}
