import { FC, useEffect, useCallback, useState } from 'react';
import { useMutation, useInfiniteQuery, useQueryClient } from 'react-query';
import { useParams } from 'react-router-dom';

import { useDebounce } from '@material-hu/hooks/useDebounce';
import Box from '@material-hu/mui/Box';
import Divider from '@material-hu/mui/Divider';

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

import typingLogo from 'src/assets/gif/typing.gif';
import { logEvent } from 'src/config/logging';
import { EVENTS_SOCKETS } from 'src/constants/sockets';
import { useAuth } from 'src/contexts/JWTContext';
import { useSocket } from 'src/contexts/SocketContext';
import useChat from 'src/hooks/queryHooks/chat';
import useGeneralError from 'src/hooks/useGeneralError';
import useLastMessageInView from 'src/hooks/useLastMessageInView';
import useUnreadChats from 'src/hooks/useUnreadChats';
import { Status } from 'src/pages/dashboard/tickets/components';
import { getChatMessages } from 'src/services/chats';
import { assignForm, changeStatusForm } from 'src/services/forms';
import { EventName } from 'src/types/amplitude';
import { FormPossibleStatus } from 'src/types/forms';
import { MessageReactionSocket } from 'src/types/reaction';
import {
  isReadOnlyChat,
  getAction,
  canMakeAction,
  iAmResponsible,
  canAddMessage,
  addLastMessageChat,
  getMessages,
} from 'src/utils/chats';
import { useLokaliseTranslation } from 'src/utils/i18n';

import {
  ChatMessages,
  ChatThreadToolbar,
  ChatThreadContainer,
  ChatThreadFooter,
  ChatThreadStatus,
} from 'src/components/dashboard/chat/ChatThread';
import {
  formKeys,
  updateListsForms,
  addFormMessageDataReaction,
  removeFormMessageDataReaction,
  setFormMessageListData,
  setFormDetailData,
  setFormData,
  removeFormChatNotification,
  editFormMessage,
} from 'src/components/dashboard/form/queries';

export type FormThreadProps = {};

const FormThread: FC<FormThreadProps> = () => {
  const { id } = useParams();
  const showGeneralError = useGeneralError();
  const { t } = useLokaliseTranslation('forms');
  const queryClient = useQueryClient();
  const socket = useSocket();
  const { user, instance } = useAuth();
  const unread = useUnreadChats();

  const { messagesEndRef, setRef, inViewMessagesEnd } = useLastMessageInView();

  const { data, isLoading, otherUser } = useChat(formKeys.detail(id), id);

  const assignMutation = useMutation(assignForm, {
    onSuccess: response => {
      const { data: thread } = response;
      addMessage(thread);
      updateListsForms();
      removeFormChatNotification();
      setFormDetailData(Number(id), thread);
    },
    onError: error => {
      showGeneralError(error, t('ERROR_ASSIGNING_FORM'));
    },
  });
  const closeMutation = useMutation(
    ({ formId, body }: any) => changeStatusForm(formId, body),
    {
      onSuccess: response => {
        const { data: thread } = response;
        const templateId = thread.form?.templateId;

        logEvent(EventName.FORM_REGULAR_CLOSE, {
          formFilledId: thread.id,
          formId: templateId,
        });

        addMessage(thread);
        updateListsForms();
        setFormDetailData(Number(id), thread);
      },
      onError: error => {
        showGeneralError(error, t('ERROR_CLOSING_FORM'));
      },
    },
  );

  const [typing, setTyping] = useState(false);
  const [typingCounter, setTypingCounter] = useState(0);

  const debounceTypingCounter = useDebounce(typingCounter, 2000);

  useEffect(() => {
    setTyping(false);
  }, [debounceTypingCounter]);

  const {
    data: messageData,
    isFetchingNextPage,
    hasNextPage,
    fetchNextPage,
  } = useInfiniteQuery(
    formKeys.messages.list(id),
    ({ pageParam = 1 }) => getChatMessages(id, pageParam),
    {
      getNextPageParam: (lastPage, pages) => {
        const { page, totalPages } = pages[pages.length - 1]?.data || {};

        return page < totalPages ? pages.length + 1 : undefined;
      },
      onError: err => showGeneralError(err, t('ERROR_LOADING_MESSAGES')),
    },
  );

  const messages = getMessages(messageData);

  useEffect(() => {
    const clearUnreadMessages = () => {
      setFormData(Number(id), formItem => {
        if (formItem.unreadMessages > 0) {
          formItem.unreadMessages = 0;
          queryClient.invalidateQueries(formKeys.redBubblesAgent());
        }
        unread.delete(Number(id));

        return formItem;
      });
    };

    clearUnreadMessages();

    return () =>
      queryClient.removeQueries(formKeys.messages.list(id), { exact: true });
  }, [id]);

  const addMessage = useCallback(
    formItem => {
      if (formItem.chat.id === Number(id)) {
        setFormMessageListData(Number(id), oldData => {
          if (!oldData) return oldData;

          if (canAddMessage(formItem, oldData.items)) {
            oldData.items = addLastMessageChat(oldData.items, formItem);
          }

          return oldData;
        });
      }
    },
    [id],
  );

  useEffect(() => {
    const setUserTyping = typingData => {
      if (
        typingData.typingUser.id !== user.id &&
        Number(id) === typingData.chatId
      ) {
        setTypingCounter(preTypingCounter => preTypingCounter + 1);
        setTyping(true);
      }
    };

    socket.listenEvent(EVENTS_SOCKETS.NEW_FORM_CHAT_MESSAGE_AGENT, addMessage);

    socket.listenEvent(EVENTS_SOCKETS.NEW_FORM_CHAT_MESSAGE_USER, addMessage);

    socket.listenEvent(EVENTS_SOCKETS.TYPING, setUserTyping);

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.NEW_FORM_CHAT_MESSAGE_AGENT, addMessage);

      socket.closeEvent(EVENTS_SOCKETS.NEW_FORM_CHAT_MESSAGE_USER, addMessage);

      socket.closeEvent(EVENTS_SOCKETS.TYPING, setUserTyping);
    };
  }, [socket, id, addMessage, instance.id, user.id]);

  useEffect(() => {
    const addMessageReaction = (reaction: MessageReactionSocket) => {
      const { emoji, unified, messageId, chatId, userId } = reaction;

      if (chatId === Number(id)) {
        addFormMessageDataReaction(
          chatId,
          messageId,
          emoji,
          unified,
          userId === user.id,
        );
      }
    };

    const deleteOrEditMessage = messageItem => {
      if (messageItem.chatId === Number(id)) {
        editFormMessage(messageItem);
      }
    };

    const removeMessageReaction = (reaction: MessageReactionSocket) => {
      const { emoji, messageId, chatId, userId } = reaction;

      if (chatId === Number(id)) {
        removeFormMessageDataReaction(
          chatId,
          messageId,
          emoji,
          userId === user.id,
        );
      }
    };

    socket.listenEvent(EVENTS_SOCKETS.NEW_MESSAGE_REACTION, addMessageReaction);

    socket.listenEvent(
      EVENTS_SOCKETS.REMOVE_MESSAGE_REACTION,
      removeMessageReaction,
    );

    socket.listenEvent(EVENTS_SOCKETS.MESSAGE_EDITED, deleteOrEditMessage);

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.NEW_MESSAGE_REACTION,
        addMessageReaction,
      );

      socket.closeEvent(
        EVENTS_SOCKETS.REMOVE_MESSAGE_REACTION,
        removeMessageReaction,
      );

      socket.closeEvent(EVENTS_SOCKETS.MESSAGE_EDITED, deleteOrEditMessage);
    };
  }, [socket, id, user.id]);

  const handleAssign = () => {
    const formId = data?.data?.id;
    assignMutation.mutate(Number(formId));
  };

  const handleClose = () => {
    const formId = data?.data?.id;
    const body = { status: FormPossibleStatus.CLOSED };
    closeMutation.mutate({ formId, body });
  };

  const {
    chat = undefined,
    topic = undefined,
    status = undefined,
    form = undefined,
    chatType = undefined,
  } = data?.data || {};

  const action = getAction(status);
  const withAction = canMakeAction(action, data?.data, user.id, otherUser);
  const toAssign = action === 'ASSIGNED';
  const isResponsible = iAmResponsible(data?.data, user.id, otherUser);

  const actionHandlers = {
    ASSIGNED: handleAssign,
    CLOSED: handleClose,
  };

  const actionTitles = {
    ASSIGNED: t('ASSIGN_ME_FORM'),
    CLOSED: t('CLOSE_FORM'),
  };

  return (
    <ChatThreadContainer inViewMessagesEnd={inViewMessagesEnd}>
      {!isLoading && chat && !!messages.length && (
        <>
          <ChatThreadToolbar
            thread={data?.data}
            otherUser={otherUser}
          />
          {data?.data?.form?.withInProgressStatus && (
            <ChatThreadStatus thread={data?.data} />
          )}
          {!data?.data?.form?.withInProgressStatus && (
            <Status
              withAction={toAssign || withAction}
              onAction={actionHandlers[action]}
              actionTitle={actionTitles[action]}
            />
          )}
          <Box className="messagesContainer">
            <ChatMessages
              id={Number(id)}
              formData={form}
              messages={messages}
              chatType={chatType}
              chatStatus={status}
              unreadMessages={chat?.unreadMessages}
              markedAsUnread={chat?.markedAsUnread}
              topic={topic}
              isResponsible={isResponsible}
              isFetchingNextPage={isFetchingNextPage}
              hasNextPage={hasNextPage}
              fetchNextPage={fetchNextPage}
              messagesEndRef={messagesEndRef}
              setRef={setRef}
              inViewMessagesEnd={inViewMessagesEnd}
              end={
                toAssign && (
                  <Divider>
                    <Button
                      variant="outlined"
                      onClick={handleAssign}
                    >
                      {t('ASSIGN_ME_FORM')}
                    </Button>
                  </Divider>
                )
              }
            />
          </Box>
          {typing && (
            <Box
              component="img"
              src={typingLogo}
              className="typingLogo"
            />
          )}
          {!isReadOnlyChat(status, chatType, isResponsible) && (
            <ChatThreadFooter
              messageAddProps={{
                id: Number(id),
                chatType,
              }}
            />
          )}
        </>
      )}
    </ChatThreadContainer>
  );
};

export default FormThread;
