import { useState, useRef, forwardRef } from 'react';
import Linkify from 'react-linkify';
import { useMutation } from 'react-query';
import { useMatch } from 'react-router-dom';

import 'src/components/dashboard/chat/i18n';

import Box from '@material-hu/mui/Box';
import Typography from '@material-hu/mui/Typography';

import { logEvent } from 'src/config/logging';
import { useAuth } from 'src/contexts/JWTContext';
import { useReplyMessage } from 'src/contexts/ReplyMessageContext';
import {
  addTicketMessageDataReaction,
  removeTicketMessageDataReaction,
} from 'src/pages/dashboard/tickets/queries';
import {
  getMessageReactionUsers,
  addMessageReaction,
  removeMessageReaction,
} from 'src/services/chats';
import { EventName } from 'src/types/amplitude';
import { Attachment as AttachmentType, FileTypes } from 'src/types/attachments';
import {
  MessageStatus,
  Message,
  Sender,
  MessageType,
  ChatType,
} from 'src/types/chats';
import { FormListItem, FormType } from 'src/types/forms';
import { Reaction } from 'src/types/reaction';
import { Topic } from 'src/types/tickets';
import { User } from 'src/types/user';
import {
  hasMenu,
  hasReactionPicker,
  hasReactionList,
  getTextFontSize,
  getShowMessageInfoCenter,
  getShowMessageInfoForFiles,
  getIsMessageWithAction,
} from 'src/utils/chats';
import { canAddReaction } from 'src/utils/reactions';
import { cn } from 'src/utils/styles';

import Attachment from 'src/components/attachment/Attachment';
import FullMediaCarousel from 'src/components/attachment/FullMediaCarousel';
import {
  RegularChatAvatar,
  TicketChatAvatar,
} from 'src/components/dashboard/chat/ChatAvatar';
import { RegularChatName } from 'src/components/dashboard/chat/ChatName';
import {
  MessageInfo,
  ChatShowForm,
  MessageActions,
  MessageReplied,
  ChatDescriptionAction,
} from 'src/components/dashboard/chat/ChatThread';
import { ChatEditMessage } from 'src/components/dashboard/chat/ChatThread/MessageActions/MessageMenu/actions';
import ReactionActions from 'src/components/dashboard/chat/ChatThread/MessageActions/ReactionsAction';
import {
  addChatMessageDataReaction,
  removeChatMessageDataReaction,
  getMessage,
} from 'src/components/dashboard/chat/queries';
import { chatSkeletonRoutes } from 'src/components/dashboard/chat/routes';
import {
  addFormMessageDataReaction,
  removeFormMessageDataReaction,
} from 'src/components/dashboard/form/queries';
import { formSkeletonRoutes } from 'src/components/dashboard/form/routes';
import { ReactionsList } from 'src/components/dashboard/reactions';

const REACTIONS_LIMIT = 10;

export type ChatMessageProps = {
  body: string;
  contentType: string;
  createdAt: Date;
  senderType: Sender;
  sender: User;
  senderId: number;
  topic: Topic;
  attachment?: AttachmentType;
  chatType: ChatType;
  chatStatus: string;
  formData?: FormListItem;
  status?: MessageStatus;
  messageId: number | string;
  chatId: number;
  dateToShow?: any;
  reactions: Reaction[];
  isInTheChat?: boolean;
  isResponsible?: boolean;
  replyMessage: Message;
  onVisible?: (messageId: number | string, isVisible: boolean) => void;
};

export const ChatMessage = forwardRef<HTMLInputElement, ChatMessageProps>(
  function ChatMessage(props, ref) {
    const {
      body,
      contentType,
      createdAt,
      sender,
      senderId,
      senderType,
      topic,
      attachment,
      chatType,
      chatStatus,
      formData,
      status = MessageStatus.SUCCESS,
      messageId,
      chatId,
      dateToShow,
      reactions,
      isInTheChat = true,
      isResponsible = false,
      replyMessage,
    } = props;

    const [expandMedia, setExpandMedia] = useState<boolean>(false);
    const [isDialogOpen, setIsDialogOpen] = useState(false);
    const [hover, setHover] = useState<boolean>(false);
    const [hoverReactionPicker, setHoverReactionPicker] =
      useState<boolean>(false);
    const [editMessage, setEditMessage] = useState<boolean>(false);

    const { setReply } = useReplyMessage();

    const {
      user: { id: authorId },
    } = useAuth();
    const messageRef = useRef<HTMLDivElement>(null);

    const addMutation = useMutation(({ emoji, unified }: any) =>
      addMessageReaction(chatId, messageId, emoji, unified),
    );

    const removeMutation = useMutation(emoji =>
      removeMessageReaction(chatId, messageId, emoji),
    );

    const handleOpenDialog = () => {
      setIsDialogOpen(true);
    };

    const matchRegularChat = useMatch(chatSkeletonRoutes.thread.detail());
    const matchFormChat = useMatch(formSkeletonRoutes.form.chat(FormType.FORM));
    const isChat = matchRegularChat || matchFormChat;

    const getReactionUsers = emoji =>
      getMessageReactionUsers(chatId, messageId, emoji);

    const openInputToEditMessage = () => {
      setEditMessage(true);
    };

    const closeInputToEditMessage = () => {
      setEditMessage(false);
    };

    const handleMouseOver = () => {
      if (!editMessage) setHover(true);
    };

    const handleMouseLeave = () => {
      if (!isDialogOpen) {
        setHover(false);
      }
    };

    const handleMouseOverReactionPicker = () => {
      if (!editMessage) setHoverReactionPicker(true);
    };

    const handleMouseLeaveReactionPicker = () => {
      setHoverReactionPicker(false);
    };

    const handleMenuClose = () => {
      setIsDialogOpen(false);
    };

    const handleReactionPickerClose = () => {
      setHoverReactionPicker(false);
    };

    const renderBody = () => {
      if (
        contentType === MessageType.FILE ||
        contentType === MessageType.AUDIO
      ) {
        return (
          <Attachment
            messageId={messageId}
            attachment={attachment}
            isLoading={status === 'PENDING'}
            onClick={(): void => setExpandMedia(true)}
            sender={sender}
          />
        );
      }

      if (contentType === MessageType.BUTTON) {
        return (
          <ChatShowForm
            isResponsible={isResponsible}
            formData={formData}
          />
        );
      }

      const fontSize = getTextFontSize(body);

      return (
        <>
          {!editMessage && (
            <Typography
              sx={theme => ({
                fontSize,
                lineHeight: 'inherit',
                px: 1,
                whiteSpace: 'pre-wrap',
                overflowWrap: 'anywhere',
                color: theme.palette.new.text.neutral.default,
              })}
            >
              {body}
            </Typography>
          )}
          {editMessage && (
            <ChatEditMessage
              body={body}
              closeInputToEditMessage={closeInputToEditMessage}
              messageId={messageId}
              chatId={chatId}
            />
          )}
        </>
      );
    };

    const handleAddReaction = async (emoji, unified) => {
      if (!canAddReaction(reactions, emoji)) return;

      await addMutation.mutateAsync({ emoji, unified });

      switch (chatType) {
        case ChatType.FORM:
          isChat
            ? addChatMessageDataReaction(chatId, messageId, emoji, unified)
            : addFormMessageDataReaction(chatId, messageId, emoji, unified);
          break;
        case ChatType.TICKET:
          isChat
            ? addChatMessageDataReaction(chatId, messageId, emoji, unified)
            : addTicketMessageDataReaction(chatId, messageId, emoji, unified);
          break;
        default:
          addChatMessageDataReaction(chatId, messageId, emoji, unified);
      }

      logEvent(EventName.CHAT_MESSAGE_REACTION, {
        chatId,
        messageId,
        reaction: emoji,
        authorId,
      });
    };

    const handleRemoveReaction = async emoji => {
      await removeMutation.mutateAsync(emoji);

      switch (chatType) {
        case ChatType.FORM:
          isChat
            ? removeChatMessageDataReaction(chatId, messageId, emoji)
            : removeFormMessageDataReaction(chatId, messageId, emoji);
          break;
        case ChatType.TICKET:
          isChat
            ? removeChatMessageDataReaction(chatId, messageId, emoji)
            : removeTicketMessageDataReaction(chatId, messageId, emoji);
          break;
        default:
          removeChatMessageDataReaction(chatId, messageId, emoji);
      }
    };

    // este codigo es necesario para que funcione el onMouseOver y onMouseLeave ver: https://github.com/facebook/react/issues/4492
    if (
      !isDialogOpen &&
      hover &&
      messageRef?.current?.matches(':hover') === false
    ) {
      setHover(false);
    }

    const withReactionPicker = hasReactionPicker(
      contentType,
      status,
      chatStatus,
      chatType,
      isResponsible,
    );
    const withReactionList = hasReactionList(contentType, status);

    const handleOnClick = e => {
      if (e.target === e.currentTarget && e.detail === 2) {
        const message = getMessage(
          chatId,
          messageId,
          chatType,
          !!matchFormChat,
        );
        if (message) setReply(message);
      }
    };

    const getMessageInfoWidth = () => {
      if (senderType === 'user') {
        return '44px';
      }
      return '30px';
    };

    const withoutReactions = !reactions || reactions.length === 0;

    const notFileAndAudioAttachment =
      attachment &&
      ![FileTypes.FILE, FileTypes.AUDIO].includes(attachment.type);

    const showMessageInfoForFiles = getShowMessageInfoForFiles(
      contentType,
      status,
      notFileAndAudioAttachment,
      withoutReactions,
    );

    const withMargin =
      (!withReactionList || withoutReactions) &&
      notFileAndAudioAttachment &&
      !replyMessage &&
      ![MessageStatus.FORWARDED, MessageStatus.EDITED].includes(status);

    return (
      <Linkify
        componentDecorator={(decoratedHref, decoratedText, key) => (
          <a
            target="_blank"
            rel="noreferrer"
            href={decoratedHref}
            key={key}
          >
            {decoratedText}
          </a>
        )}
      >
        {dateToShow && (
          <Box
            sx={{
              display: 'flex',
              width: '100%',
              justifyContent: 'center',
              mb: 2,
            }}
          >
            <Typography
              color="textSecondary"
              variant="subtitle2"
              sx={theme => ({
                padding: '5px 12px 6px',
                backgroundColor: theme.palette.new.background.layout.tertiary,
                boxShadow: '0px 1px 0.5px #0b141a21',
                borderRadius: '8px',
              })}
            >
              {dateToShow}
            </Typography>
          </Box>
        )}
        <Box
          sx={{
            width: '100%',
            '& img': {
              borderRadius: '8px',
            },
            '& video': {
              borderRadius: '8px',
            },
          }}
          onClick={handleOnClick}
          onMouseOver={handleMouseOverReactionPicker}
          onMouseLeave={handleMouseLeaveReactionPicker}
        >
          <Box
            ref={ref}
            sx={{ pointerEvents: 'none' }}
            className={cn([
              'message',
              senderType === 'user' ? 'loggedUser' : 'otherUser',
              editMessage ? 'edit' : 'readOnly',
              contentType === MessageType.BUTTON ? 'button' : 'regular',
            ])}
          >
            {chatType !== ChatType.REGULAR && (
              <Box
                className="avatar"
                sx={{ pointerEvents: 'auto' }}
              >
                {sender ? (
                  <RegularChatAvatar
                    otherUser={sender}
                    otherUserId={senderId}
                    withInitials={
                      !isInTheChat && chatType === ChatType.REGULAR_GROUP
                    }
                    size="small"
                  />
                ) : (
                  <TicketChatAvatar topic={topic} />
                )}
              </Box>
            )}
            <Box
              ref={messageRef}
              onMouseOver={handleMouseOver}
              onMouseLeave={handleMouseLeave}
              sx={{
                pointerEvents: 'auto',
                maxWidth: '450px',
                overflowWrap: 'break-word',
                px: attachment ? 0 : 2,
                position: 'relative',
                p: getShowMessageInfoCenter(
                  contentType,
                  status,
                  replyMessage,
                  withoutReactions,
                )
                  ? '8px 8px 8px 0px!important'
                  : 'auto',
              }}
              className={cn(['bubble', replyMessage ? 'reply' : undefined])}
            >
              {chatType === ChatType.REGULAR_GROUP &&
                senderType !== 'user' &&
                sender && (
                  <RegularChatName
                    otherUser={sender}
                    otherUserId={senderId}
                    className="name"
                    sx={{
                      width: 'fit-content',
                      marginBottom: '2px !important',
                      marginLeft: '8px !important',
                      '& a': {
                        marginBottom: '0 !important',
                        color: sender.color,
                        fontSize: '13px',
                        fontWeight: 500,
                        textDecoration: 'none',
                        '&:hover': {
                          textDecoration: 'underline',
                          color: '#172b4d !important',
                        },
                      },
                      '& p': {
                        fontSize: '13px',
                        fontWeight: 600,
                      },
                    }}
                  />
                )}

              <Box
                sx={{
                  position:
                    contentType === MessageType.FILE ? 'relative' : 'unset',
                  display: getShowMessageInfoCenter(
                    contentType,
                    status,
                    replyMessage,
                    withoutReactions,
                  )
                    ? 'flex'
                    : 'block',
                  alignItems: 'center',
                  justifyContent: 'space-between',
                }}
              >
                {getIsMessageWithAction(status) && (
                  <ChatDescriptionAction status={status} />
                )}
                {hover &&
                  contentType === MessageType.FILE &&
                  status !== MessageStatus.DELETED && (
                    <Box
                      sx={{
                        position: 'absolute',
                        background:
                          'linear-gradient(15deg,#0b141a00,#0b141a00 40%,#0b141a3d 65%,#0b141a88)',
                        top: '0px',
                        right: '0px',
                        width: '156px',
                        maxWidth: '90%',
                        height: '40px',
                        opacity: 1,
                        zIndex: 2,
                        overflow: 'hidden',
                        borderTopRightRadius: '8px',
                      }}
                    >
                      <MessageActions
                        sender={sender}
                        messageId={messageId}
                        chatId={chatId}
                        chatType={chatType}
                        attachment={attachment}
                        openInputToEditMessage={openInputToEditMessage}
                        onOpenDialog={handleOpenDialog}
                        status={status}
                        contentType={contentType}
                        onCloseMenu={handleMenuClose}
                        withMenu={hasMenu(
                          chatType,
                          chatStatus,
                          !!matchFormChat,
                        )}
                        isSender={authorId === sender?.id}
                        isFile
                      />
                    </Box>
                  )}
                {hover &&
                  ![MessageType.FILE, MessageType.BUTTON].includes(
                    contentType as MessageType,
                  ) && (
                    <MessageActions
                      sender={sender}
                      messageId={messageId}
                      chatId={chatId}
                      chatType={chatType}
                      attachment={attachment}
                      openInputToEditMessage={openInputToEditMessage}
                      onOpenDialog={handleOpenDialog}
                      status={status}
                      contentType={contentType}
                      onCloseMenu={handleMenuClose}
                      withMenu={hasMenu(chatType, chatStatus, !!matchFormChat)}
                      isSender={authorId === sender?.id}
                    />
                  )}
                {hoverReactionPicker &&
                  (reactions?.length < REACTIONS_LIMIT || !reactions) && (
                    <ReactionActions
                      isSender={authorId === sender?.id}
                      onAddReaction={handleAddReaction}
                      onCloseReactionPicker={handleReactionPickerClose}
                      withReactionPicker={withReactionPicker}
                    />
                  )}
                {status !== MessageStatus.DELETED && (
                  <>
                    {replyMessage && (
                      <MessageReplied
                        message={replyMessage}
                        withScroll
                      />
                    )}
                    {renderBody()}
                  </>
                )}
                {withReactionList && (
                  <ReactionsList
                    id={chatId}
                    reactions={reactions}
                    getReactionUsers={getReactionUsers}
                    onAdd={handleAddReaction}
                    onRemove={handleRemoveReaction}
                    withAdd={reactions?.length > 0 && withReactionPicker}
                    disabled={!withReactionPicker}
                    limit={REACTIONS_LIMIT}
                    stackProps={{
                      sx: {
                        mt: 0.5,
                      },
                    }}
                  />
                )}
                {showMessageInfoForFiles && (
                  <Box
                    sx={{
                      position: 'absolute',
                      backgroundImage:
                        'linear-gradient(to top,#0b141a80,#0b141a00)',
                      zIndex: 2,
                      height: '28px',
                      width: '100%',
                      bottom: 0,
                      left: 0,
                      borderBottomRightRadius: '8px',
                      borderBottomLeftRadius: '8px',
                      display: 'flex',
                      justifyContent: 'flex-end',
                      alignItems: 'flex-end',
                      paddingRight: '8px',
                    }}
                  >
                    <MessageInfo
                      className="info"
                      createdAt={createdAt}
                      status={status}
                      senderType={senderType}
                      isFile
                    />
                  </Box>
                )}
                {!showMessageInfoForFiles && (
                  <Box
                    sx={{
                      width: getShowMessageInfoCenter(
                        contentType,
                        status,
                        replyMessage,
                        withoutReactions,
                      )
                        ? getMessageInfoWidth()
                        : '100%',
                      display: 'flex',
                      justifyContent: 'flex-end',
                      marginBottom: withMargin ? '-10px' : '0px',
                    }}
                  >
                    <MessageInfo
                      className="info"
                      createdAt={createdAt}
                      status={status}
                      senderType={senderType}
                    />
                  </Box>
                )}
              </Box>
            </Box>
            {expandMedia && (
              <Box sx={{ pointerEvents: 'auto' }}>
                <FullMediaCarousel
                  open={expandMedia}
                  mediaList={[attachment]}
                  showName
                  showDownload
                  onClose={(): void => setExpandMedia(false)}
                />
              </Box>
            )}
          </Box>
        </Box>
      </Linkify>
    );
  },
);

export default ChatMessage;
