import { useRef } from 'react';

import IconButton from '@material-hu/mui/IconButton';
import Input, { InputProps } from '@material-hu/mui/Input';

import useSnackbar from '@material-hu/components/design-system/Snackbar';
import HuTooltip from '@material-hu/components/design-system/Tooltip';
import { TooltipProps } from '@material-hu/components/design-system/Tooltip/types';

import { MAX_ATTACHED } from 'src/constants/attachments';
import useHuGoTheme from 'src/hooks/useHuGoTheme';
import { getDimensions } from 'src/services/attachments';
import { FormFile as FormFileType, FileTypes } from 'src/types/attachments';
import { compressFile } from 'src/utils/files';
import { useLokaliseTranslation } from 'src/utils/i18n';

export const NEW_WIDTH = 1000;
export const NEW_HEIGHT = 1000;

type MultimediaFile = File & {
  width?: number;
  height?: number;
};

type FormFileProps = InputProps &
  Pick<TooltipProps, 'direction'> & {
    title: string;
    type: string;
    icon: any;
    size?: any;
    iconSx?: any;
    onChange?: (file: any) => void;
    disabled?: boolean;
    inputProps?: any;
    multiple?: boolean;
    multimediaSize?: number;
    filesSize?: number;
    isFileMultiple?: boolean;
    maxMultimedia?: number;
    maxFiles?: number;
    checkMax?: boolean;
    onlyIcon?: boolean;
  };

const FormFile = ({
  name,
  title,
  type,
  icon,
  size = 'large',
  disabled = false,
  direction = 'bottom',
  inputProps,
  onChange,
  multiple = false,
  multimediaSize = 0,
  filesSize = 0,
  isFileMultiple = false,
  maxMultimedia = MAX_ATTACHED.MULTIMEDIA,
  maxFiles = MAX_ATTACHED.FILES,
  checkMax = true,
  onlyIcon = false,
  ...others
}: FormFileProps) => {
  const fileInputRef = useRef<HTMLInputElement | null>(null);
  const { t } = useLokaliseTranslation('web_only');
  const HugoThemeProvider = useHuGoTheme();
  const { enqueueSnackbar } = useSnackbar();

  const handleAttach = (): void => {
    fileInputRef.current?.click();
  };

  const checkMultipleFilesUpload = (files: FileList) => {
    return (
      (files.length <= maxMultimedia - multimediaSize && !isFileMultiple) ||
      (files.length <= maxFiles - filesSize && isFileMultiple)
    );
  };

  const createImageFormFile = async (
    compressedFile: File,
  ): Promise<FormFileType> => {
    const file = new File([compressedFile], compressedFile.name);
    const dimensions = await getDimensions(file);
    const newFile: FormFileType = {
      name: file?.name,
      size: file?.size,
      mime: compressedFile?.type,
      width: dimensions?.width,
      height: dimensions?.height,
      fileObject: file,
      type: FileTypes.IMAGE,
      toUpload: true,
    };
    return newFile;
  };

  const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const inputFile = event.target.files?.[0] as MultimediaFile;

    if (!inputFile) {
      return;
    }

    if (type === FileTypes.IMAGE) {
      try {
        compressFile(inputFile).then(createImageFormFile).then(onChange);
      } catch (err) {
        enqueueSnackbar({
          title: t('form_inputs.compress_image_error'),
          variant: 'error',
        });
      }
    } else if (inputFile) {
      const newFile: FormFileType = {
        name: inputFile?.name,
        size: inputFile?.size,
        mime: inputFile?.type,
        width: inputFile?.width,
        height: inputFile?.height,
        fileObject: inputFile,
        type,
        toUpload: true,
      };

      onChange?.(newFile);
    }
  };

  const handleChangeMultiple = async (
    event: React.ChangeEvent<HTMLInputElement>,
  ) => {
    const { files } = event.target;

    if (!files) {
      return;
    }

    const filePromises: Promise<FormFileType>[] = [];

    if (!checkMax || checkMultipleFilesUpload(files)) {
      for (let i = 0; i < files.length; i++) {
        if (files[i].type.includes('image') && !isFileMultiple) {
          filePromises.push(compressImage(files[i]));
        } else if (files[i].type.includes('video') && !isFileMultiple) {
          filePromises.push(addVideo(files[i]));
        } else if (files[i].type.includes('audio') && !isFileMultiple) {
          filePromises.push(addAudio(files[i]));
        } else {
          filePromises.push(addFile(files[i]));
        }
      }

      Promise.all(filePromises).then(compressedFiles => {
        onChange?.(compressedFiles);
      });
    } else {
      enqueueSnackbar({
        title: isFileMultiple
          ? t('form_inputs.upload_limit_files', { count: maxFiles })
          : t('form_inputs.upload_limit_multimedia', { count: maxMultimedia }),
        variant: 'warning',
      });
    }
  };

  const addFile = (inputFile: MultimediaFile): Promise<FormFileType> =>
    new Promise(resolve => {
      const newFile: FormFileType = {
        name: inputFile?.name,
        size: inputFile?.size,
        mime: inputFile?.type,
        width: inputFile?.width,
        height: inputFile?.height,
        fileObject: inputFile,
        type,
        toUpload: true,
      };
      resolve(newFile);
    });

  const addVideo = (inputFile: MultimediaFile): Promise<FormFileType> =>
    new Promise(resolve => {
      const tempVideo = document.createElement('video');
      tempVideo.preload = 'metadata';

      tempVideo.src = URL.createObjectURL(inputFile);

      tempVideo.onloadedmetadata = () => {
        URL.revokeObjectURL(tempVideo.src); // cleanup
        resolve({
          name: inputFile?.name,
          size: inputFile?.size,
          mime: inputFile?.type,
          width: tempVideo.videoWidth,
          height: tempVideo.videoHeight,
          fileObject: inputFile,
          type,
          toUpload: true,
        });
      };
    });

  const addAudio = (inputFile: MultimediaFile): Promise<FormFileType> =>
    new Promise(resolve => {
      const tempAudio = document.createElement('audio');
      tempAudio.preload = 'metadata';
      tempAudio.src = URL.createObjectURL(inputFile);

      tempAudio.onloadedmetadata = () => {
        const durationInMs = Math.round(tempAudio.duration * 1000);
        URL.revokeObjectURL(tempAudio.src);
        resolve({
          name: inputFile?.name,
          size: inputFile?.size,
          mime: inputFile?.type,
          fileObject: inputFile,
          type: FileTypes.AUDIO,
          toUpload: true,
          metadata: { durationInMs },
        });
      };
    });

  const compressImage = (inputFile: MultimediaFile): Promise<FormFileType> =>
    new Promise(resolve => {
      try {
        compressFile(inputFile).then(createImageFormFile).then(resolve);
      } catch (err) {
        enqueueSnackbar({
          title: t('form_inputs.compress_image_error'),
          variant: 'error',
        });
      }
    });

  const { sx: inputSx = {}, iconSx = {}, ...inputWrapperProps } = others;

  return (
    <HugoThemeProvider>
      <HuTooltip
        title={title}
        direction={direction}
        disableTooltip={disabled}
      >
        <span>
          <IconButton
            onClick={handleAttach}
            disabled={disabled}
            sx={{
              ...(onlyIcon && {
                p: 0,
                '&:hover': { backgroundColor: 'transparent' },
              }),
              ...iconSx,
            }}
            size={size}
          >
            {icon}
          </IconButton>
        </span>
      </HuTooltip>
      <Input
        {...inputWrapperProps}
        type="file"
        sx={{
          display: 'none',
          ...inputSx,
        }}
        disabled={disabled}
        inputRef={fileInputRef}
        onChange={multiple ? handleChangeMultiple : handleChange}
        inputProps={inputProps}
      />
    </HugoThemeProvider>
  );
};

export default FormFile;
