/**
 * @deprecated Use `@material-hu/components/design-system/Uploader/form` instead
 */
import { type FC, useEffect, useState } from 'react';
import { useFieldArray, useFormContext } from 'react-hook-form';
import { type TFunction, Trans } from 'react-i18next';
import { useMutation } from 'react-query';

import {
  type ErrorCode,
  type FileRejection,
  useDropzone,
} from '@humanddev/material-hu/dropzone';
import { omit } from 'lodash-es';
import { useModal } from '@material-hu/hooks/useModal';
import Add from '@material-hu/icons/material/Add';
import Close from '@material-hu/icons/material/Close';
import UploadFile from '@material-hu/icons/material/UploadFile';
import FormHelperText from '@material-hu/mui/FormHelperText';
import Stack from '@material-hu/mui/Stack';
import { alpha, useTheme } from '@material-hu/mui/styles';
import Typography from '@material-hu/mui/Typography';

import Button from '@material-hu/components/design-system/Buttons/Button';

import {
  bytesToSize,
  fileToAttachment,
  getReadableSignedUrl,
  signedUpload,
} from 'src/utils/filesUtils';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { idMaker } from 'src/utils/idMaker';

import AllowedAttachmentsModal from './AllowedAttachmentsModal';
import AttachmentItem from './AttachmentItem';
import { ACCEPT, MAX_SIZE } from './constants';
import {
  type Accept,
  FormDropStatus,
  type FormDropValue,
  type UploadData,
  type UploadResponse,
} from './types';

const idGen = idMaker();

export type FormDropAttachmentsProps = {
  name: string;
  disabled?: boolean;
  maxSize?: number;
  maxFiles?: number;
  multiple?: boolean;
  accept?: Accept;
  exclude?: Accept;
  onDropAccepted?: (files: File[]) => void;
  onRemove?: (index: number) => void;
  onRetry?: (index: number, drop: FormDropValue) => void;
  onEditName?: (index: number, name: string) => void;
  onDropRejected?: (fileRejections: FileRejection[]) => void;
  dropZoneProps?: any;
};

export const FormDropAttachments: FC<FormDropAttachmentsProps> = props => {
  const {
    name,
    disabled = false,
    maxSize = MAX_SIZE,
    maxFiles = Infinity,
    multiple = true,
    accept: acceptProp = ACCEPT,
    exclude = {},
    onDropAccepted = () => null,
    onRemove = () => null,
    onRetry = () => null,
    onEditName = () => null,
    onDropRejected = () => null,
    dropZoneProps,
  } = props;

  const [showDropZone, setShowDropZone] = useState(false);

  const { control, setError, getValues, clearErrors, getFieldState } =
    useFormContext();
  const theme = useTheme();
  const { t } = useLokaliseTranslation(['backoffice_only']);

  const { fields, append, update, remove } = useFieldArray({ name, control });

  const errorMessage = getFieldState(name)?.error?.message;
  const files = fields as unknown as FormDropValue[];
  const accept = omit(acceptProp, Object.keys(exclude));

  useEffect(() => {
    if (showDropZone) {
      const dropzoneElement = document.getElementById(
        'form-drop-attachments-dropzone',
      );
      dropzoneElement?.scrollIntoView({ behavior: 'smooth', block: 'center' });
    }
  }, [showDropZone]);

  const uploadMutation = useMutation(
    async ({ drop }: UploadData): Promise<UploadResponse> => {
      const attachment = drop.attachment;
      if (!attachment.file) return attachment;

      const file: File = attachment.file;
      const signedUrl = await signedUpload(file);
      const resourceUrl = await getReadableSignedUrl(signedUrl);

      return {
        ...attachment,
        url: attachment.url || resourceUrl,
      };
    },
    {
      onMutate: ({ drop, index }) => {
        update(index, {
          ...drop,
          status: FormDropStatus.LOADING,
          isError: false,
          isLoading: true,
          isSuccess: false,
        });
      },
      onSuccess: (response: UploadResponse, { drop, index }) => {
        update(index, {
          ...drop,
          status: FormDropStatus.SUCCESS,
          isError: false,
          isLoading: false,
          isSuccess: true,
          attachment: response,
        });
      },
      onError: (_, { drop, index }) => {
        update(index, {
          ...drop,
          status: FormDropStatus.ERROR,
          isError: true,
          isLoading: false,
          isSuccess: false,
        });
      },
    },
  );

  const handleDropAccepted = (newFiles: File[]) => {
    onDropAccepted(newFiles);

    setShowDropZone(false);

    newFiles.forEach(async file => {
      const attachment = await fileToAttachment(file);

      append({
        id: `${idGen.next().value}-internal`,
        isError: false,
        isLoading: false,
        isSuccess: false,
        status: FormDropStatus.IDLE,
        attachment,
      });

      const updatedFiles = getValues(name);

      uploadMutation.mutate({
        drop: updatedFiles[updatedFiles.length - 1],
        index: updatedFiles.length - 1,
      });
    });
  };

  const handleRetry = (retryIndex: number) => (drop: FormDropValue) => {
    onRetry(retryIndex, drop);
    uploadMutation.mutate({ drop, index: retryIndex });
  };

  const handleRemove = (deletedIndex: number) => () => {
    onRemove(deletedIndex);
    remove(deletedIndex);
  };

  const handleEditName = (editedIndex: number) => (newName: string) => {
    onEditName(editedIndex, newName);

    const drop = files[editedIndex];

    update(editedIndex, {
      ...drop,
      attachment: {
        ...drop.attachment,
        name: newName,
      },
    });
  };

  const handleDropRejected = (fileRejections: FileRejection[]) => {
    onDropRejected(fileRejections);

    if (!fileRejections?.length && !fileRejections[0]?.errors?.length) return;

    const errorCode = fileRejections[0].errors[0].code as ErrorCode;

    setError(name, {
      type: 'custom',
      message: t('backoffice_only:form_drop_attachments.error', {
        context: errorCode,
        max: bytesToSize(maxSize),
        count: maxFiles,
      }),
    });
  };

  const { getRootProps, getInputProps, open } = useDropzone({
    onDropAccepted: handleDropAccepted,
    onDropRejected: handleDropRejected,
    accept,
    maxFiles,
    maxSize,
    multiple,
    ...dropZoneProps,
  });

  const allowedTypesModal = useModal(
    AllowedAttachmentsModal,
    { fullWidth: true, maxWidth: 'sm' },
    { accept },
  );

  const handleAllowedTypesClick = (
    e: React.MouseEvent<HTMLButtonElement, MouseEvent>,
  ) => {
    e.preventDefault();
    e.stopPropagation();
    allowedTypesModal.showModal();
  };

  const toggleFileDrop = (newOpen: boolean) => () => {
    setShowDropZone(newOpen);
    clearErrors(name);
  };

  return (
    <Stack
      sx={{
        gap: theme.spacing(2),
        p: theme.spacing(2),
        backgroundColor: '#F8F9FA',
        borderRadius: theme.spacing(1),
      }}
    >
      {allowedTypesModal.modal}
      {files.map((file, index) => (
        <AttachmentItem
          key={file.id}
          onRemove={handleRemove(index)}
          onEditNameConfirm={handleEditName(index)}
          onRetry={handleRetry(index)}
          maxSize={maxSize}
          drop={file}
        />
      ))}
      {showDropZone && (
        <Stack
          component="label"
          id="form-drop-attachments-dropzone"
          sx={{
            width: 1,
            alignItems: 'center',
            borderRadius: 0.5,
            border: '1px dashed #0000001F',
            backgroundColor: 'background.paper',
            mx: 'auto',
            cursor: 'pointer',
            py: 4,
            gap: 1,
          }}
          {...getRootProps()}
        >
          <input {...getInputProps()} />
          <Stack
            sx={{
              alignItems: 'center',
              justifyContent: 'center',
              height: '40px',
              width: '40px',
              borderRadius: '50%',
              backgroundColor: alpha(theme.palette.primary.main, 0.12),
            }}
          >
            <UploadFile sx={{ color: theme.palette.primary.main }} />
          </Stack>
          <Typography variant="subtitle1">
            <Trans
              i18nKey={t('backoffice_only:form_drop_attachments.label')}
              t={t as TFunction}
              components={{
                button: (
                  <Button
                    variant="text"
                    onClick={e => {
                      e.stopPropagation();
                      open();
                    }}
                    sx={{ p: 0 }}
                  />
                ),
              }}
            />
          </Typography>
          <Typography
            variant="body2"
            color="text.secondary"
          >
            <Trans
              i18nKey={t('backoffice_only:form_drop_attachments.help_text', {
                max: bytesToSize(maxSize),
              })}
              t={t as TFunction}
              components={{
                button: (
                  <Button
                    onClick={handleAllowedTypesClick}
                    variant="text"
                    sx={{ p: 0 }}
                  />
                ),
              }}
            />
          </Typography>
          {!!errorMessage && (
            <FormHelperText
              error
              id="drop-picture-error-text"
            >
              {errorMessage}
            </FormHelperText>
          )}
        </Stack>
      )}
      <Stack
        sx={{
          flexDirection: 'row',
          justifyContent: 'flex-start',
          alignItems: 'center',
        }}
      >
        {showDropZone && (
          <Button
            id="form-drop-attachments-close-button"
            variant="text"
            color="primary"
            onClick={toggleFileDrop(!showDropZone)}
            startIcon={
              <Close
                fontSize="small"
                color="primary"
              />
            }
          >
            <Typography variant="subtitle2">{t('general:close')}</Typography>
          </Button>
        )}
        {!showDropZone && (
          <Button
            id="form-drop-attachments-add-button"
            variant="text"
            color="primary"
            onClick={toggleFileDrop(!showDropZone)}
            disabled={disabled}
            startIcon={
              <Add
                fontSize="small"
                color="primary"
              />
            }
          >
            <Typography variant="subtitle2">
              {t('form_drop_attachments.add_attachment')}
            </Typography>
          </Button>
        )}
      </Stack>
    </Stack>
  );
};

export default FormDropAttachments;
