import { memo, useCallback, useEffect, 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 { useDebounce } from '@material-hu/hooks/useDebounce';
import Stack, { type StackProps } from '@material-hu/mui/Stack';

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

import { useAuth } from 'src/contexts/JWTContext';
import useGeneralError from 'src/hooks/useGeneralError';
import useHuGoTheme from 'src/hooks/useHuGoTheme';
import { uploadAllImages } from 'src/services/attachments';
import { notifyArticleCommentUsers } from 'src/services/news';
import { notifyPostCommentUsers } from 'src/services/posts';
import {
  type Attachment,
  type FormFile as FormFileType,
} from 'src/types/attachments';
import { type PostCommentPayload } from 'src/types/comments';
import { type TaggedUser, type User } from 'src/types/user';
import {
  getFileToPaste,
  hasFileToPaste,
  isValidFilesSize,
} from 'src/utils/attachments';
import { getCommentType } from 'src/utils/comments';
import {
  clearComposerDraft,
  readComposerDraft,
  writeComposerDraft,
} from 'src/utils/composerDrafts';
import { createGifBody, extractTextFromHTML } from 'src/utils/feed';
import { getAttachment } from 'src/utils/files';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { getFullName } from 'src/utils/userUtils';

import AttachmentsAdd from 'src/components/attachment/AttachmentsAdd';
import AttachmentsList from 'src/components/attachment/AttachmentsList';
import ReactionAdd from 'src/components/dashboard/reactions/ReactionAdd';
import ErrorDialog from 'src/components/dialogs/ErrorDialog';
import ProfilePicture from 'src/components/user/ProfilePicture';

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

export type CommentAddProps = Omit<StackProps, 'id' | 'onSubmit'> & {
  id: number;
  onSubmit: (
    formData: PostCommentPayload,
    taggedUsers?: TaggedUser[],
  ) => Promise<void> | void;
  maxAttachmentsBytes?: number | null;
  isPost?: boolean;
  isCommentReply?: boolean;
  setInitialFocus?: boolean;
  childRef?: any;
  onBlur?: () => void;
  replyUser?: User | null;
  onDeleteUserReply?: () => void;
  isNewTheme?: boolean;
  draftKey?: string | null;
};

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

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

const CommentAdd = ({
  id,
  onSubmit,
  maxAttachmentsBytes = null,
  isPost = false,
  isCommentReply = false,
  setInitialFocus = false,
  onBlur = () => {},
  replyUser = null,
  onDeleteUserReply = () => null,
  isNewTheme = false,
  draftKey = null,
  ...rest
}: CommentAddProps) => {
  const [images, setImages] = useState<FormFileType[]>([]);
  const [gif, setGif] = useState<IGif>();
  const [openError, setOpenError] = useState<boolean>(false);
  const [editorReady, setEditorReady] = useState(false);
  const [initialContent, setInitialContent] = useState('');
  const editorRef = useRef<CommentRichTextInputRef>(null);
  const replyInsertedRef = useRef(false);
  const hasHydratedDraftRef = useRef(false);
  const handleEditorReady = useCallback(() => setEditorReady(true), []);
  const HugoThemeProvider = useHuGoTheme();
  const { t } = useLokaliseTranslation(['web_only', 'post']);
  const { enqueueSnackbar } = useSnackbar();
  const { user: loggedUser } = useAuth();
  const location = useLocation();
  const showGeneralError = useGeneralError();

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

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

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

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

  const bodyHtml = watch('bodyHtml');
  const debouncedBodyHtml = useDebounce(bodyHtml);
  const hasImages = images?.length > 0;
  const hasAttachments = images?.length > 0 || !!gif;
  const imagesAttach = useMemo(() => images.map(getAttachment), [images]);

  useEffect(() => {
    if (!draftKey) {
      return;
    }

    const savedDraft = readComposerDraft(draftKey);
    if (!savedDraft) {
      return;
    }

    hasHydratedDraftRef.current = true;
    setInitialContent(savedDraft);
    // Use setValue to avoid mutating defaultValues; keep form pristine after hydration.
    setValue('bodyHtml', savedDraft, {
      shouldDirty: false,
      shouldTouch: false,
    });
    editorRef.current?.editor?.commands.setContent(savedDraft);
  }, [draftKey, setValue]);

  useEffect(() => {
    if (setInitialFocus) {
      editorRef.current?.editor?.commands.focus();
    }
  }, [setInitialFocus]);

  useEffect(() => {
    if (
      replyUser &&
      !replyInsertedRef.current &&
      !hasHydratedDraftRef.current
    ) {
      const editor = editorRef.current?.editor;
      if (!editor) return;

      replyInsertedRef.current = true;
      const fullName = getFullName(replyUser);
      editor.commands.setContent('');
      editor
        .chain()
        .focus()
        .insertContent([
          {
            type: 'mention',
            attrs: { id: replyUser.id.toString(), label: fullName },
          },
          { type: 'text', text: ' ' },
        ])
        .run();
    }
    if (!replyUser) {
      replyInsertedRef.current = false;
    }
  }, [replyUser, editorReady]);

  const resetForm = () => {
    reset();
    editorRef.current?.editor?.commands.clearContent();
    setInitialContent('');
    setImages([]);
    setGif(undefined);
    hasHydratedDraftRef.current = false;
  };

  useEffect(() => {
    if (!draftKey) {
      return;
    }

    const value = debouncedBodyHtml ?? '';
    const { plainText } = extractTextFromHTML(value);
    const hasContent = !!plainText.trim() || !!value.trim();
    const liveValue = bodyHtml ?? '';
    const { plainText: livePlainText } = extractTextFromHTML(liveValue);
    const liveHasContent = !!livePlainText.trim() || !!liveValue.trim();

    // If we just hydrated a saved draft and debounce hasn't caught up yet,
    // skip clearing to avoid deleting the existing draft on mount.
    if (hasHydratedDraftRef.current && !hasContent && liveHasContent) {
      return;
    }
    if (hasHydratedDraftRef.current && (hasContent || !liveHasContent)) {
      hasHydratedDraftRef.current = false;
    }

    if (!hasContent) {
      clearComposerDraft(draftKey);
      return;
    }

    writeComposerDraft(draftKey, value);
  }, [draftKey, debouncedBodyHtml, bodyHtml]);

  const submit = async (values: { bodyHtml: string }) => {
    const { plainText, mentions } = extractTextFromHTML(values.bodyHtml);
    const validMentions = mentions.filter(
      (mention): mention is TaggedUser => mention.type.user.id !== null,
    );

    try {
      if (!isValidAttachments()) {
        openErrorDialog();
        return;
      }

      const uploadImages = await uploadAllImages(images);
      const gifBody = createGifBody(gif);
      const attachments: Attachment[] = [gifBody, ...uploadImages].filter(
        (attachment): attachment is Attachment => Boolean(attachment),
      );

      const formData: PostCommentPayload = {
        body: plainText,
        ...(!!validMentions?.length && {
          bodyAttributes: validMentions,
          taggedUserIds: validMentions.map(mention => mention.type.user.id),
        }),
        attachments,
        parentId: isCommentReply ? id : undefined,
      };
      await Promise.resolve(onSubmit(formData, validMentions));
      if (draftKey) {
        clearComposerDraft(draftKey);
      }
      resetForm();
    } catch (error) {
      showGeneralError(error, t('web_only:comments.add_comment_error'));
      return;
    }

    const type = getCommentType(location.pathname);

    if (validMentions?.length && type === 'post') {
      await notifyPostMutation.mutateAsync({
        postId: id,
        taggedUsers: validMentions,
      });
    } else if (validMentions?.length && type === 'article') {
      await notifyArticleMutation.mutateAsync({
        articleId: id,
        taggedUsers: validMentions,
      });
    }
  };

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

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

    return isValidFilesSize(images, maxAttachmentsBytes);
  };

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

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

  const handleDeleteImage = (_attachment: Attachment, index: number) => {
    const newList = [...images];
    newList.splice(index, 1);

    setImages(newList);
  };

  const handlePaste = async (event: React.ClipboardEvent<HTMLFormElement>) => {
    const clipboardEvent =
      event as unknown as React.ClipboardEvent<HTMLInputElement>;
    if (!hasFileToPaste(clipboardEvent)) return;

    event.preventDefault();

    if (hasImages) {
      enqueueSnackbar({
        title: t('web_only:comments.add_many_pictures_error'),
        variant: 'error',
      });
    } else {
      const newFile = await getFileToPaste(clipboardEvent);
      if (newFile) {
        handleAddImage(newFile);
      }
    }
  };

  const handleAddEmoji = (emoji: string) => {
    const editor = editorRef.current?.editor;
    if (editor) {
      editor.chain().focus().insertContent(emoji).run();
    }
  };

  const getPlaceholder = () => {
    if (isPost) return t('post:add_comment');
    if (isCommentReply) return t('post:add_response');
    return undefined;
  };

  const showButton = bodyHtml || hasAttachments;

  return (
    <HugoThemeProvider>
      {maxAttachmentsBytes && (
        <ErrorDialog
          open={openError}
          onAccept={closeErrorDialog}
          message={t('post:error_max_attachments_size')}
        />
      )}
      <Stack
        sx={{
          py: 2,
          pl: isCommentReply ? 5 : undefined,
          pt: isCommentReply ? 0 : undefined,
        }}
        {...rest}
      >
        <FormProvider {...form}>
          <form
            noValidate
            onSubmit={handleSubmit(submit)}
            style={{ width: '100%' }}
            onPaste={handlePaste}
          >
            <Stack sx={{ flexDirection: 'row', alignItems: 'center', gap: 1 }}>
              {loggedUser && (
                <ProfilePicture
                  id={loggedUser.id}
                  user={loggedUser}
                  sx={{
                    width: isCommentReply ? '32px' : '40px',
                    height: isCommentReply ? '32px' : '40px',
                    fontSize: isCommentReply ? '16px' : '20px',
                  }}
                />
              )}
              <CommentRichTextInput
                ref={editorRef}
                name="bodyHtml"
                placeholder={getPlaceholder()}
                disabled={isSubmitting}
                onBlur={onBlur}
                onReady={handleEditorReady}
                initialContent={initialContent}
                endAdornment={
                  <Stack
                    sx={{
                      flexDirection: 'row',
                      alignItems: 'center',
                      gap: 0.5,
                    }}
                  >
                    <ReactionAdd
                      onAdd={handleAddEmoji}
                      variant="icon"
                    />
                    <AttachmentsAdd
                      onAddImage={handleAddImage}
                      onlyIcon={true}
                      onAddGif={handleAddGif}
                      disabled={
                        isSubmitting || !isValidAttachments() || hasAttachments
                      }
                    />
                  </Stack>
                }
              />
            </Stack>
            {hasAttachments && (
              <Stack
                sx={{
                  backgroundColor: theme =>
                    theme.palette.new.background.layout.default,
                  borderRadius: 2,
                  p: 1.5,
                  ml: 6,
                }}
              >
                <AttachmentsList
                  images={imagesAttach}
                  gif={gif}
                  onDelete={handleDeleteImage}
                  onRemoveGif={handleRemoveGif}
                  disabled={isSubmitting}
                  isComment
                />
              </Stack>
            )}
            {showButton && (
              <Stack
                alignItems="flex-end"
                sx={{ mt: 1 }}
              >
                <Button
                  variant="primary"
                  type="submit"
                  disabled={isSubmitting}
                  loading={isSubmitting}
                >
                  {isCommentReply
                    ? t('web_only:comments.reply_comment')
                    : t('post:publish')}
                </Button>
              </Stack>
            )}
          </form>
        </FormProvider>
      </Stack>
    </HugoThemeProvider>
  );
};

export default memo(CommentAdd);
