import {
  FC,
  useMemo,
  useState,
  useEffect,
  Dispatch,
  SetStateAction,
} from 'react';
import { useMutation } from 'react-query';

import AddPhotoIcon from '@material-hu/icons/material/AddAPhoto';
import VideocamIcon from '@material-hu/icons/material/Videocam';
import Box from '@material-hu/mui/Box';
import CircularProgress from '@material-hu/mui/CircularProgress';
import Stack from '@material-hu/mui/Stack';

import useSnackbar from '@material-hu/components/design-system/Snackbar';

import useGeneralError from 'src/hooks/useGeneralError';
import { uploadImage, uploadVideo } from 'src/services/attachments';
import { FileTypes, FormFile } from 'src/types/attachments';
import { isValidVideosSize } from 'src/utils/attachments';
import { getAttachment } from 'src/utils/files';
import { useLokaliseTranslation } from 'src/utils/i18n';

import {
  AttachmentsAdd,
  AttachmentsList,
  MediaCarousel,
} from 'src/components/attachment';

export type MediaInputProps = {
  id?: string;
  onChange: (values: any) => void;
  answerInput?: any[];
  backendImages?: { images: FormFile[]; videos: FormFile[] };
  inputFile?: any;
  disabled?: boolean;
  setUploadingFilesBoolean?: (value: boolean) => void; // This is needed to maintain retrocompatibility with the old version of the component
  setUploadingFiles?: Dispatch<SetStateAction<Record<string, boolean>>>;
  name?: string;
  loading?: boolean;
};

const MediaInput: FC<MediaInputProps> = ({
  id,
  onChange,
  inputFile,
  answerInput,
  disabled,
  backendImages = undefined,
  setUploadingFilesBoolean = null,
  setUploadingFiles = null,
  loading = false,
  name = '',
}) => {
  const [images, setImages] = useState(inputFile?.images || []);
  const [videos, setVideos] = useState(inputFile?.videos || []);

  const { t } = useLokaliseTranslation('forms');
  const showGeneralError = useGeneralError();
  const { enqueueSnackbar } = useSnackbar();

  const setFileUpload = (add: boolean) => {
    if (setUploadingFilesBoolean) {
      setUploadingFilesBoolean(add);
    } else if (setUploadingFiles) {
      add
        ? setUploadingFiles(prev => ({
            ...prev,
            [name]: true,
          }))
        : setUploadingFiles(prev => {
            const newUploadingFiles = { ...prev };
            delete newUploadingFiles[name];
            return newUploadingFiles;
          });
    }
  };

  const { mutateAsync: uploadImageForm } = useMutation(
    (image: any) => {
      setFileUpload(true);
      return uploadImage(image);
    },
    {
      onError: err => {
        showGeneralError(err, t('NO_UPLOAD_FILES'));
        setFileUpload(false);
      },
      onSuccess: () => {
        setFileUpload(false);
      },
    },
  );

  const { mutateAsync: uploadVideoForm } = useMutation(
    (video: any) => {
      setFileUpload(true);
      return uploadVideo(video);
    },
    {
      onError: err => {
        showGeneralError(err, t('NO_UPLOAD_FILES'));
        setFileUpload(false);
      },
      onSuccess: () => {
        setFileUpload(false);
      },
    },
  );

  useEffect(() => {
    // This serves to make images that come from the back visible to
    // allow them to be deleted in case the user wants to
    if (backendImages?.images.length || backendImages?.videos.length) {
      setImages(backendImages?.images);
      setVideos(backendImages?.videos);
    }
  }, [backendImages?.images.length, backendImages?.videos.length]);

  useEffect(() => {
    if (videos.length === 0 && images.length === 0) {
      onChange(undefined);
    }
  }, [videos, images, onChange]);

  const handleAddImage = async image => {
    const imageFormated = await uploadImageForm(image);
    setImages([...images, image]);

    onChange({
      videos: inputFile?.videos,
      images: [
        ...(Array.isArray(inputFile?.images) ? inputFile?.images : []),
        {
          ...image,
          url: imageFormated.url,
          size: imageFormated.size,
        },
      ],
    });
  };

  const handleAddVideo = async video => {
    if (!isValidVideosSize(video)) {
      enqueueSnackbar({
        title: t('ERROR_MAX_ATTACHMENTS_SIZE'),
        variant: 'error',
      });
      return;
    }

    const videoFormated = await uploadVideoForm(video);
    setVideos([...videos, video]);

    onChange({
      images: inputFile?.images,
      videos: [
        ...(Array.isArray(inputFile?.videos) ? inputFile?.videos : []),
        {
          ...video,
          url: videoFormated.url,
          size: videoFormated.size,
        },
      ],
    });
  };

  const handleDelete = (attachment, index) => {
    if (attachment.type === FileTypes.IMAGE) {
      const newList = [...images];
      newList.splice(index, 1);
      setImages(newList);
      onChange({ videos: inputFile?.videos, images: newList });
    } else {
      const newList = [...videos];
      newList.splice(index, 1);
      setVideos(newList);
      onChange({ images: inputFile?.images, videos: newList });
    }
  };

  const imagesAttach = useMemo(() => images.map(getAttachment), [images]);
  const videosAttach = useMemo(() => videos.map(getAttachment), [videos]);

  const hasAttachments = () =>
    imagesAttach?.length > 0 || videosAttach?.length > 0;

  return (
    <>
      <Stack
        sx={{
          textAlign: 'center',
          gap: 1,
          flexDirection: 'row',
          alignItems: 'center',
          justifyContent: 'center',
        }}
        id={id}
      >
        <AttachmentsAdd
          disabled={disabled}
          iconCustomPhoto={
            <AddPhotoIcon
              fontSize="large"
              sx={{ mx: 4 }}
            />
          }
          iconCustomVideo={
            <VideocamIcon
              fontSize="large"
              sx={{ mx: 4 }}
            />
          }
          onAddImage={handleAddImage}
          onAddVideo={handleAddVideo}
        />
        {loading && <CircularProgress size={16} />}
      </Stack>
      {hasAttachments() && (
        <AttachmentsList
          sx={{ my: 2, mx: 2 }}
          images={imagesAttach}
          videos={videosAttach}
          onDelete={handleDelete}
        />
      )}
      {answerInput?.length > 0 && (
        <Box sx={{ my: 2 }}>
          <MediaCarousel mediaList={answerInput} />
        </Box>
      )}
    </>
  );
};

export default MediaInput;
