import {
  Button,
  Classes,
  Dialog,
  DialogBody,
  DialogFooter,
  FileInput,
  InputGroup,
  Label,
} from '@blueprintjs/core'
import { useHost } from '@inject/graphql/connection/host'
import { ReloadDefinitions } from '@inject/graphql/mutations.client'
import {
  uploadDefinitionUrl,
  validateDefinitionUrl,
} from '@inject/shared/config'
import { dialog } from '@inject/shared/css/dialog'
import { notify } from '@inject/shared/notification/engine'
import authenticatedFetch from '@inject/shared/utils/authenticatedFetch'
import type { ChangeEvent, Dispatch, FC, SetStateAction } from 'react'
import { useCallback, useMemo, useState } from 'react'
import { useClient } from 'urql'

interface DefinitionUploaderProps {
  open: boolean
  setOpen: Dispatch<SetStateAction<boolean>>
}

const DefinitionUploader: FC<DefinitionUploaderProps> = ({ open, setOpen }) => {
  const client = useClient()
  const [name, setName] = useState<string>('')
  const [file, setFile] = useState<File | undefined>()

  const reset = useCallback(() => {
    setOpen(false)
    setName('')
    setFile(undefined)
  }, [setOpen])

  const host = useHost()

  const [loadingSubmit, setLoadingSubmit] = useState<boolean>(false)
  const [loadingValidate, setLoadingValidate] = useState<boolean>(false)

  const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
    if (e.target.files) {
      setFile(e.target.files[0])
    }
  }

  const handleSubmit = () => {
    if (!file) {
      return
    }

    const data = new FormData()
    data.append('file', file)
    if (name) {
      data.append('definition_name', name)
    }

    setLoadingSubmit(true)
    authenticatedFetch(uploadDefinitionUrl(host || ''), {
      method: 'POST',
      body: data,
      credentials: 'include',
    })
      .then(res => res.json())
      .then((res: { status: string; detail: string }) => {
        if (res.status === 'error') {
          notify(res.detail, { intent: 'danger' })
          return
        }
        reset()
        /*
         * it would be more sane to rewrite this into a graphql function,
         * graphql SUPPORT file upload, and it should return graphql definition struct
         */
        client.mutation(ReloadDefinitions, {}).toPromise()
      })
      .finally(() => setLoadingSubmit(false))
  }

  const placeholder = useMemo(
    () => file?.name.replace(/\.zip$/, '') || 'Definition name',
    [file?.name]
  )

  const handleValidate = useCallback(() => {
    if (!file) {
      return
    }

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

    setLoadingValidate(true)
    authenticatedFetch(validateDefinitionUrl(host || ''), {
      method: 'POST',
      body: data,
      credentials: 'include',
    })
      .then(res => res.json())
      .then((res: { status: string; detail: string }) => {
        if (res.status === 'error') {
          notify(res.detail, { intent: 'danger' })
          return
        }
        notify(res.detail, { intent: 'success' })
      })
      .finally(() => setLoadingValidate(false))
  }, [file, host])

  return (
    <Dialog
      className={dialog}
      isOpen={open}
      onClose={reset}
      icon='upload'
      title='Upload a definition'
    >
      <DialogBody>
        <Label style={{ width: '100%' }}>
          Name <span className={Classes.TEXT_MUTED}>(optional)</span>
          <InputGroup
            placeholder={placeholder}
            value={name}
            onChange={e => setName(e.target.value)}
          />
        </Label>
        <Label style={{ width: '100%' }}>
          File
          <div
            style={{ display: 'flex', gap: '0.5rem', alignItems: 'flex-end' }}
          >
            <FileInput
              className={Classes.INPUT}
              fill
              hasSelection={file !== undefined}
              text={file ? file.name : 'Choose file...'}
              onInputChange={handleFileChange}
            />

            <Button
              disabled={!file}
              title={file ? '' : 'Upload a file to validate'}
              onClick={handleValidate}
              loading={loadingValidate}
            >
              Validate
            </Button>
          </div>
        </Label>
      </DialogBody>
      <DialogFooter
        actions={
          <Button
            onClick={handleSubmit}
            intent='primary'
            disabled={!file}
            title={file ? '' : 'Upload a definition file'}
            loading={loadingSubmit}
          >
            Submit
          </Button>
        }
      />
    </Dialog>
  )
}

export default DefinitionUploader
