import { useMemo, useRef, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useMutation } from 'react-query';
import { useLocation } from 'react-router';

import { type IGif } from '@giphy/js-types';
import { isEqual } from 'lodash-es';
import Container from '@material-hu/mui/Container';
import Stack from '@material-hu/mui/Stack';
import { useTheme } from '@material-hu/mui/styles';
import useMediaQuery from '@material-hu/mui/useMediaQuery';

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

import useGeneralError from 'src/hooks/useGeneralError';
import { uploadAllImages } from 'src/services/attachments';
import { updateEventPostComment } from 'src/services/events';
import { updateGroupPostComment } from 'src/services/groups';
import { notifyArticleCommentUsers } from 'src/services/news';
import { notifyPostCommentUsers, updateComment } from 'src/services/posts';
import { type FormFile as FormFileType } from 'src/types/attachments';
import { type Comment } from 'src/types/comments';
import { type TaggedUser } from 'src/types/user';
import { isValidFilesSize } from 'src/utils/attachments';
import { getCommentType } from 'src/utils/comments';
import { createGifBody, extractTextFromHTML } from 'src/utils/feed';
import { getAttachment } from 'src/utils/files';
import { useLokaliseTranslation } from 'src/utils/i18n';

import AttachmentsAdd from 'src/components/attachment/AttachmentsAdd';
import AttachmentsList from 'src/components/attachment/AttachmentsList';
import AttachmentsSize from 'src/components/attachment/AttachmentsSize';
import ErrorDialog from 'src/components/dialogs/ErrorDialog';

import { usePost } from '../post/PostContext';

import CommentRichTextInput, {
  buildEditorContent,
  type CommentRichTextInputRef,
} from './CommentRichTextInput';

export type CommentEditProps = {
  commentId: number;
  postId: number;
  defaultText: string;
  defaultImages: FormFileType[];
  defaultGif?: IGif;
  defaultBodyAttributes?: TaggedUser[];
  onEdit: (commentData: Comment) => void;
  onCancel: any;
  maxAttachmentsBytes?: number;
  withoutAttachments?: boolean;
};

const notifyPostUserWrap = ({
  postId,
  taggedUsers,
}: {
  postId: number;
  taggedUsers: TaggedUser[];
}) => notifyPostCommentUsers(postId, taggedUsers);

const notifyArticleUserWrap = ({
  articleId,
  taggedUsers,
}: {
  articleId: number;
  taggedUsers: TaggedUser[];
}) => notifyArticleCommentUsers(articleId, taggedUsers);

const CommentEdit = ({
  commentId,
  postId,
  defaultText = '',
  defaultImages = [],
  defaultGif,
  defaultBodyAttributes = [],
  onEdit,
  onCancel,
  maxAttachmentsBytes,
  withoutAttachments = false,
}: CommentEditProps) => {
  const [images, setImages] = useState<FormFileType[]>(defaultImages);
  const [gif, setGif] = useState(defaultGif);
  const [openError, setOpenError] = useState<boolean>(false);
  const editorRef = useRef<CommentRichTextInputRef>(null);

  const { t } = useLokaliseTranslation(['web_only', 'post', 'general']);
  const location = useLocation();
  const showGeneralError = useGeneralError();
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
  const { groupId, eventId } = usePost();

  const initialContent = useMemo(
    () => buildEditorContent(defaultText, defaultBodyAttributes),
    [defaultText, defaultBodyAttributes],
  );

  const updateMutation = useMutation((body: any) => {
    if (eventId) {
      return updateEventPostComment(eventId, postId, commentId, body);
    }
    if (groupId) {
      return updateGroupPostComment(groupId, postId, commentId, body);
    }
    return updateComment(commentId, body);
  });

  const notifyPostMutation = useMutation(notifyPostUserWrap, {
    onError: err => showGeneralError(err, t('web_only:comments.notify_error')),
  });

  const notifyArticleMutation = useMutation(notifyArticleUserWrap, {
    onError: err => showGeneralError(err, t('web_only:comments.notify_error')),
  });

  const imagesAttach = useMemo(() => images.map(getAttachment), [images]);
  const hasAttachments = imagesAttach?.length > 0 || !!gif;

  const form = useForm({
    defaultValues: {
      bodyHtml: '',
    },
  });

  const {
    handleSubmit,
    reset,
    watch,
    formState: { isSubmitting },
  } = form;

  const bodyHtml = watch('bodyHtml');

  const resetForm = () => reset();

  const currentPlainText = useMemo(
    () => (bodyHtml ? extractTextFromHTML(bodyHtml).plainText : ''),
    [bodyHtml],
  );

  const hasChanges = () =>
    !isEqual(currentPlainText, defaultText) || !isEqual(images, defaultImages);

  const editComment = async (plainText: string, mentions: TaggedUser[]) => {
    try {
      const uploadImages = await uploadAllImages(images);
      const gifBody = createGifBody(gif);

      const formData = {
        body: plainText,
        bodyAttributes: mentions,
        attachments: [gifBody, ...uploadImages].filter(
          attachment => attachment,
        ),
      };

      const response = await updateMutation.mutateAsync(formData);
      resetForm();

      const { data: commentData } = response;
      onEdit(commentData);
    } catch (error) {
      showGeneralError(error, t('web_only:comments.edit_comment_error'));
    }
  };

  const type = getCommentType(location.pathname);
  const isPostComment = type === 'post';
  const isArticleComment = type === 'article';

  const notifyTaggedUsers = (mentions: TaggedUser[]) => {
    if (mentions?.length && isPostComment) {
      notifyPostMutation.mutateAsync({
        postId: commentId,
        taggedUsers: mentions,
      });
    } else if (mentions?.length && isArticleComment) {
      notifyArticleMutation.mutateAsync({
        articleId: commentId,
        taggedUsers: mentions,
      });
    }
  };

  const submit = (values: { bodyHtml: string }) => {
    if (!isValidAttachments()) {
      openErrorDialog();
      return;
    }
    const { plainText, mentions } = extractTextFromHTML(values.bodyHtml);
    editComment(plainText, mentions);
    notifyTaggedUsers(mentions);
  };

  const openErrorDialog = () => setOpenError(true);
  const closeErrorDialog = () => setOpenError(false);

  const isValidAttachments = (): boolean => {
    if (maxAttachmentsBytes === undefined) return true;

    return isValidFilesSize(images, maxAttachmentsBytes);
  };

  const handleCancelOperation = () => {
    setImages(defaultImages);
    setGif(defaultGif);
    onCancel();
  };

  const handleDeleteImage = (_: any, index: number) => {
    const newList = [...images];
    newList.splice(index, 1);
    setImages(newList);
  };

  const handleAddImage = (image: FormFileType) => setImages([...images, image]);

  const handleAddGif = (newGif: IGif) => setGif(newGif);
  const handleRemoveGif = () => setGif(undefined);

  const isBodyEmpty = !bodyHtml || editorRef.current?.editor?.isEmpty;

  return (
    <>
      {maxAttachmentsBytes && (
        <ErrorDialog
          open={openError}
          onAccept={closeErrorDialog}
          message={t('post:error_max_attachments_size')}
        />
      )}
      <FormProvider {...form}>
        <form
          noValidate
          onSubmit={handleSubmit(submit)}
        >
          <Container sx={{ p: '0 !important' }}>
            <CommentRichTextInput
              ref={editorRef}
              name="bodyHtml"
              placeholder={t('post:add_comment')}
              disabled={isSubmitting}
              initialContent={initialContent}
            />
            {!withoutAttachments && (
              <>
                <Stack
                  sx={{
                    flexDirection: 'row',
                    justifyContent: 'start',
                    alignItems: 'center',
                    pt: 2,
                  }}
                >
                  <AttachmentsAdd
                    onAddImage={handleAddImage}
                    gifDisabled={hasAttachments}
                    onAddGif={isPostComment ? handleAddGif : undefined}
                    disabled={isSubmitting || !isValidAttachments() || !!gif}
                  />
                </Stack>
                {maxAttachmentsBytes && (
                  <AttachmentsSize
                    sx={{ ml: 1 }}
                    images={imagesAttach}
                    maxBytes={maxAttachmentsBytes}
                  />
                )}
              </>
            )}
            <Stack
              sx={{
                justifyContent: 'space-between',
                alignItems: 'start',
              }}
            >
              {hasAttachments && (
                <AttachmentsList
                  images={imagesAttach}
                  gif={gif}
                  onDelete={handleDeleteImage}
                  onRemoveGif={handleRemoveGif}
                  disabled={isSubmitting}
                />
              )}
            </Stack>
            <Stack
              sx={{
                mt: 1,
                flexDirection: 'row',
                display: isMobile ? 'inline-grid' : 'flex',
                alignItems: 'center',
                justifyContent: 'flex-end',
                gap: 1,
              }}
            >
              <Button
                variant="secondary"
                size="small"
                onClick={handleCancelOperation}
              >
                {t('general:cancel')}
              </Button>
              <Button
                size="small"
                variant="primary"
                disabled={!hasChanges() || isBodyEmpty}
                type="submit"
              >
                {t('general:save_changes')}
              </Button>
            </Stack>
          </Container>
        </form>
      </FormProvider>
    </>
  );
};

export default CommentEdit;
