import { checkFileSize } from '@/utils'
import { Button, FileInput } from '@blueprintjs/core'
import { useHost } from '@inject/graphql/connection/host'
import type { FileInfo } from '@inject/graphql/fragment-types'
import type { ResultOf, VariablesOf } from '@inject/graphql/graphql'
import { GetFileInfo } from '@inject/graphql/queries'
import { useClient } from '@inject/graphql/urql/client'
import { uploadFileUrl } from '@inject/shared/config'
import { notify } from '@inject/shared/notification/engine'
import authenticatedFetch from '@inject/shared/utils/authenticatedFetch'
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 [loading, setLoading] = useState(false)
  const client = useClient()

  // TODO: use contextual saving of file selection, we have an API for that :tm:

  /**
   * 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 file = e.target.files[0]

      if (!checkFileSize(file)) {
        return
      }

      const formData = new FormData()
      formData.append('file', file)

      setLoading(true)
      authenticatedFetch(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
          client
            .query<
              ResultOf<typeof GetFileInfo>,
              VariablesOf<typeof GetFileInfo>
            >(GetFileInfo, { fileInfoId: res.detail })
            .then(res => setFileInfo(res.data?.fileInfo || undefined))
        })
        .catch((error: { status: string; detail: string }) => {
          notify(`${error.status}: ${error.detail}`, JSON.stringify(error), {
            intent: 'danger',
          })
        })
        .finally(() => setLoading(false))
    },
    [client, host, 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
