import FormProvider from 'domains/forms/provider'
import { useI18n } from 'domains/i18n/hooks'
import { className } from 'lib/css'
import { useCallback, useEffect, useMemo, useRef } from 'preact/hooks'
import UploadProgress from 'ui/components/widgets/upload-progress'
import { useSeamlyEntry } from 'ui/hooks/seamly-entry-hooks'
import {
  useFileUploadMeta,
  useFileUploads,
  useGeneratedId,
  useLiveRegion,
  useSkiplinkTarget,
  useSkiplinkTargetFocusing,
} from 'ui/hooks/seamly-hooks'
import useSingleFileUpload from 'ui/hooks/use-single-file-upload'
import { getValidator } from 'ui/utils/form-utils'
import { formatBytes } from 'ui/utils/general-utils'
import {
  fileListObjectIsNotEmpty,
  validateFileSize,
} from 'ui/utils/validations'
import FileInputForm from './file-upload-form'

const formName = 'fileListForm'
const fileInputName = 'fileList'

const Upload = () => {
  const { t } = useI18n()
  const { sendPolite, sendAssertive } = useLiveRegion()
  const skiplinkTargetId = useSkiplinkTarget()
  const focusSkiplinkTarget = useSkiplinkTargetFocusing()
  // This hook should be refactored at some point
  const { serviceAllowsUploads, allowedMimeTypes, maxSize } =
    useFileUploadMeta()
  const cancelButtonRef = useRef(null)
  const canUpload = useRef(serviceAllowsUploads)

  const { cancelEntrySelection } = useSeamlyEntry()
  const { uploadFile, clearUploads, isUploading, isComplete } = useFileUploads()

  const hasError = false

  const { uploadHandle, hasServerError, progress } = useSingleFileUpload(
    formName,
    fileInputName,
  )
  const notificationId = useGeneratedId()
  const prevIsComplete = useRef(true)

  const contentHintText = t('fileUpload.contentHint', {
    size: formatBytes(maxSize),
  })
  const prevContentHintText = useRef('')
  const containerRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    if (
      prevContentHintText.current &&
      containerRef.current?.contains(document.activeElement) &&
      document.activeElement?.tagName === 'INPUT'
    ) {
      sendAssertive(contentHintText)
    }
    prevContentHintText.current = contentHintText
  }, [contentHintText, sendAssertive])

  // a11y
  useEffect(() => {
    if (progress > 0) {
      switch (progress) {
        case 1:
          sendPolite(t('fileUpload.srStartedText'))
          break
        default:
          sendPolite(`${progress}%`)
      }
    }
  }, [progress, sendPolite, t])

  // reset form when uploads are complete
  useEffect(() => {
    if (!prevIsComplete.current && !isUploading && isComplete) {
      clearUploads()
      cancelEntrySelection()
      focusSkiplinkTarget()
      // This timeout is set as testing in VoiceOver revealed that 300 ms is needed for
      // the live region to resolve in this case.
      setTimeout(() => {
        sendPolite(t('fileUpload.srCompleteText'))
      }, 300)
    }
    prevIsComplete.current = isComplete
  }, [
    isUploading,
    isComplete,
    clearUploads,
    cancelEntrySelection,
    focusSkiplinkTarget,
    sendPolite,
    t,
  ])

  // Reset form when service no longer allows uploads
  useEffect(() => {
    // If we are currently uploading, don't clear the uploads as that
    // may be confusing to the user.
    // When the upload completes and the user clicks the submit button,
    // there will be an error.
    if (!serviceAllowsUploads && !isUploading) {
      clearUploads()
      cancelEntrySelection()
      focusSkiplinkTarget()
    }
  }, [
    serviceAllowsUploads,
    isUploading,
    clearUploads,
    cancelEntrySelection,
    focusSkiplinkTarget,
  ])

  const handleSubmit = useCallback(
    ({ fileList }) => {
      uploadFile(fileList[0])

      if (!hasError) {
        focusSkiplinkTarget()
      }
    },
    [uploadFile, hasError, focusSkiplinkTarget],
  )

  const handleOnClickCancel = useCallback(() => {
    if (uploadHandle) {
      uploadHandle.abort()
    }
    clearUploads()
    cancelEntrySelection()
    focusSkiplinkTarget()
  }, [uploadHandle, clearUploads, cancelEntrySelection, focusSkiplinkTarget])

  const validationSchema = useMemo(
    () => ({
      [fileInputName]: [
        // For now this is fine, but this shouldn't be a validation on the field itself
        getValidator(
          () => serviceAllowsUploads,
          t('fileUpload.errors.unavailable'),
        ),
        getValidator(fileListObjectIsNotEmpty, t('fileUpload.errors.noFile')),
        getValidator(
          validateFileSize,
          t('fileUpload.errors.tooLarge'),
          maxSize,
        ),
      ],
    }),
    [maxSize, serviceAllowsUploads, t],
  )

  return (
    <div
      className={className([
        'upload',
        ...(hasError || hasServerError ? ['upload--error'] : []),
      ])}
      ref={containerRef}
    >
      <FormProvider
        formId={formName}
        persistData={true}
        onSubmit={handleSubmit}
        validationSchema={validationSchema}
      >
        {(isUploading || !isComplete) && (
          <>
            <UploadProgress />
            <div className={className('upload__button-container')}>
              <button
                id={isUploading || !isComplete ? skiplinkTargetId : undefined}
                type="button"
                onClick={handleOnClickCancel}
                ref={cancelButtonRef}
                className={className(
                  'button',
                  'button--secondary',
                  'upload__cancel',
                )}
              >
                {t('fileUpload.cancelButtonText')}
              </button>
            </div>
          </>
        )}
        {!isUploading && isComplete && (
          <FileInputForm
            controlName={fileInputName}
            skiplinkId={skiplinkTargetId}
            accept={allowedMimeTypes?.join(', ') || ''}
            contentHint={contentHintText}
            isComplete={isComplete}
            isUploading={isUploading}
            onClickCancel={handleOnClickCancel}
          />
        )}

        {!canUpload.current && (
          <>
            <span className={className('notification')} id={notificationId}>
              {t('fileUpload.unavailableText')}
            </span>
            <div className={className('upload__button-container')}>
              <button
                id={isUploading || !isComplete ? skiplinkTargetId : undefined}
                type="button"
                onClick={handleOnClickCancel}
                className={className(
                  'button',
                  'button--secondary',
                  'upload__cancel',
                )}
              >
                {t('fileUpload.cancelButtonText')}
              </button>
            </div>
          </>
        )}
      </FormProvider>
    </div>
  )
}

export default Upload
