import { Button, FileInput } from '@blueprintjs/core'
import { useHost } from '@inject/graphql/connection/host'
import type { FileInfo } from '@inject/graphql/fragments/FileInfo.generated'
import { useGetFileInfoLazyQuery } from '@inject/graphql/queries/GetFileInfo.generated'
import { uploadFileUrl } from '@inject/shared/config'
import { useNotifyContext } from '@inject/shared/notification/contexts/NotifyContext'
import csrfFetch from '@inject/shared/utils/csrfFetch'
import type { ChangeEvent, Dispatch, FC, SetStateAction } from 'react'
import { useCallback, useState } from 'react'
import FileViewRedirectButton from '../FileViewRedirectButton'

interface FileAreaProps {
  teamId: string
  exerciseId: string
  fileInfo: FileInfo | undefined | null
  setFileInfo: Dispatch<SetStateAction<FileInfo | undefined>>
}

const FileArea: FC<FileAreaProps> = ({
  teamId,
  exerciseId,
  fileInfo,
  setFileInfo,
}) => {
  const host = useHost()
  const { notify } = useNotifyContext()
  const [loading, setLoading] = useState(false)

  const [getFileInfo] = useGetFileInfoLazyQuery()

  /**
   * Uploads the file to the backend - the file is uploaded prior to submitting the form.
   *
   * This may cause unnecessary upload of files (if the user doesn't submit the form),
   * but allows saving files in drafts.
   */
  const handleFileChange = useCallback(
    (e: ChangeEvent<HTMLInputElement>) => {
      if (!e.target.files || e.target.files.length < 1) {
        return
      }

      const formData = new FormData()
      formData.append('file', e.target.files[0])

      setLoading(true)
      csrfFetch(uploadFileUrl(host || '', teamId), {
        method: 'POST',
        body: formData,
        credentials: 'include',
      })
        .then(res => res.json())
        .then(async (res: { status: string; detail: string }) => {
          // await for a smoother transition from the loading state
          await getFileInfo({
            variables: { fileInfoId: res.detail },
            onCompleted: data => setFileInfo(data.fileInfo || undefined),
          })
        })
        .catch((err: { status: string; detail: string }) => {
          notify(`${err.status}: ${err.detail}`, {
            intent: 'danger',
          })
        })
        .finally(() => setLoading(false))
    },
    [getFileInfo, host, notify, setFileInfo, teamId]
  )

  if (loading) return <Button loading minimal />

  return fileInfo ? (
    <FileViewRedirectButton
      exerciseId={exerciseId}
      teamId={teamId}
      fileInfo={fileInfo}
      onDelete={() => setFileInfo(undefined)}
    />
  ) : (
    <FileInput text='Attach a file' onInputChange={handleFileChange} />
  )
}

export default FileArea
