import {
  type FC,
  Fragment,
  type ReactNode,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useInView } from 'react-intersection-observer';

import { styled, useTheme } from '@material-hu/mui/styles';

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

import ExpandMore from '@material-hu/icons/material/ExpandMore';
import Box from '@material-hu/mui/Box';
import CircularProgress from '@material-hu/mui/CircularProgress';
import IconButton from '@material-hu/mui/IconButton';

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

import { CHAT_CONSTANTS } from 'src/constants/chats';
import { AudioProvider } from 'src/contexts/AudioContext';
import { useAuth } from 'src/contexts/JWTContext';
import useChat from 'src/hooks/useChat';
import useScrollToMessage from 'src/hooks/useScrollToMessage';
import useSendChatMessage from 'src/hooks/useSendChatMessage';
import { uploadAllAttachments } from 'src/services/attachments';
import { sendMessage } from 'src/services/chats';
import { FileTypes } from 'src/types/attachments';
import { ChatType, type Message, MessageType } from 'src/types/chats';
import { type FormListItem } from 'src/types/forms';
import { type Topic } from 'src/types/tickets';
import {
  fileSizeToMB,
  findImageIndex,
  processAttachment,
} from 'src/utils/attachments';
import {
  createMultimediaBody,
  disabledDragAndDrop,
  getDateToShow,
  getMessageRef,
  getSender,
  isInfo,
  type Values,
} from 'src/utils/chats';
import { getAttachment } from 'src/utils/files';
import { useLokaliseTranslation } from 'src/utils/i18n';

import AttachmentConfirmDialog from 'src/components/attachment/AttachmentConfirmDialog';
import FormFileDropZoneChats from 'src/components/dashboard/chat/FormFileDropZoneChats';
import { useTranslation as useTranslationChat } from 'src/components/dashboard/chat/i18n';

import ChatInfo from './ChatInfo';
import ChatMessage from './ChatMessage';
import ShowUnreadMessages from './ShowUnreadMessages';

const MAX_SIZE_FILES = 100;

const sendMessageWrap = async (id, values: Values) => {
  const { images, files, videos } = values;

  const [uploadFiles, uploadImages, uploadVideos] = await uploadAllAttachments(
    files,
    images,
    videos,
  );

  const formData = {
    type: MessageType.FILE,
    attachments: [...uploadImages, ...uploadFiles, ...uploadVideos].filter(
      attachment => attachment,
    ),
  };

  return sendMessage(id, formData);
};

const Container = styled(Box)(() => ({
  height: '100%',
  '& .scrollbar::-webkit-scrollbar': {
    backgroundColor: 'transparent',
    width: '16px',
  },
  '& .scrollbar::-webkit-scrollbar-thumb': {
    backgroundColor: '#babac0',
    borderRadius: '16px',
    border: '5px solid #f4f4f4',
  },
  '& .scrollbar::-webkit-scrollbar-thumb:hover': {
    backgroundColor: '#a0a0a5',
    border: '4px solid #f4f4f4',
  },
  '& .scrollbar::-webkit-scrollbar-button': {
    display: 'none',
  },
}));

export type ChatMessagesProps = {
  id: number;
  messages: Message[];
  formData?: FormListItem;
  topic: Topic;
  end: ReactNode;
  isResponsible?: boolean;
  chatType: ChatType;
  isFetchingNextPage?: boolean;
  hasNextPage?: boolean;
  fetchNextPage?: Function;
  lastPage?: any;
  chatStatus: string;
  markedAsUnread: boolean;
  unreadMessages: number;
  setRef: (node) => void;
  inViewMessagesEnd: boolean;
  messagesEndRef: any;
};

export const ChatMessages: FC<ChatMessagesProps> = props => {
  const theme = useTheme();
  const {
    id,
    messages,
    topic,
    end,
    isResponsible = false,
    chatType,
    formData,
    isFetchingNextPage,
    hasNextPage,
    fetchNextPage,
    lastPage,
    chatStatus,
    markedAsUnread,
    unreadMessages,
    setRef,
    inViewMessagesEnd,
    messagesEndRef,
  } = props;

  const lastMessageRef = useRef(null);
  const firstMessageUnreadRef = useRef(null);

  const messageToScrollToRef = useRef(null);
  const { clearUnreadChats } = useChat();

  const {
    user,
    instance: { allowAttachmentsInChats },
  } = useAuth();

  const { t } = useLokaliseTranslation();
  const { t: tChat } = useTranslationChat();
  const { enqueueSnackbar } = useSnackbar();

  const [enableGoToLastMessageRef, setEnableGoToLastMessageRef] =
    useState(true);

  const form = useForm({
    defaultValues: {
      images: [],
      files: [],
      videos: [],
    },
  });

  const { watch, setValue, handleSubmit } = form;

  const [loadMoreRef, inViewLoadMoreRef] = useInView({
    threshold: 0.1,
  });

  const { messageToScrollTo, scrollToMessage } = useScrollToMessage(
    fetchNextPage,
    messageToScrollToRef,
  );

  const resetAttachments = () => {
    setValue('images', []);
    setValue('files', []);
    setValue('videos', []);
  };

  const onMutateSendMessage = () => {
    resetAttachments();
  };

  const onSuccessSendMessage = () => {
    resetAttachments();
  };

  const { sendMessageMutate, isLoading } = useSendChatMessage(
    id,
    chatType,
    sendMessageWrap,
    onMutateSendMessage,
    onSuccessSendMessage,
  );

  const scrollToBottom = () => {
    messagesEndRef.current.scrollIntoView();
  };

  useEffect(() => {
    if (unreadMessages || markedAsUnread) {
      clearUnreadChats();
    }
    if (!unreadMessages) {
      scrollToBottom();
    }
  }, [id]);

  useEffect(() => {
    if (messages.length < unreadMessages) {
      setEnableGoToLastMessageRef(false);
      fetchNextPage();
    } else {
      scrollToFirstMessageUnread();
      setEnableGoToLastMessageRef(true);
    }
  }, [messages.length, unreadMessages]);

  useEffect(() => {
    if (messageToScrollTo) {
      scrollToMessage();
    }
  }, [lastPage, scrollToMessage, messageToScrollTo]);

  useEffect(() => {
    if (unreadMessages === 0) {
      scrollToBottom();
    }
  }, [messages[messages.length - 1]]);

  useEffect(() => {
    // si se llega arriba de todo se hace un fetch a la siguiente pagina
    if (inViewLoadMoreRef && hasNextPage) {
      fetchNextPage();
    }
  }, [inViewLoadMoreRef, hasNextPage]);

  useEffect(() => {
    // la variable enableGoToLastMessageRef permite que no haya problema
    // al ir hacia el ultimo unreadMessage si esto no esta al redirigirse al
    // primer mensaje sin leer va a querer ir hacia el lastMessageRef que no seria correcto
    if (
      !isFetchingNextPage &&
      messages.length > CHAT_CONSTANTS.MESSAGES_LIMIT_PER_PAGE &&
      enableGoToLastMessageRef &&
      !messageToScrollTo
    ) {
      lastMessageRef.current?.scrollIntoView({ block: 'center' });
    }
  }, [isFetchingNextPage]);

  const scrollToFirstMessageUnread = () => {
    firstMessageUnreadRef.current?.scrollIntoView({ block: 'center' });
  };

  const sendAttachment = values => {
    const { images, videos, files } = values;
    const multimedia = [...images, ...videos, ...files];
    multimedia.forEach(file =>
      sendMessageMutate({ body: '', ...createMultimediaBody(file) }),
    );
  };

  const images = watch('images');
  const videos = watch('videos');
  const files = watch('files');

  const getAttachmentValue = (type: string) => {
    switch (type) {
      case FileTypes.IMAGE:
        return { list: images, name: 'images' } as const;
        break;
      case FileTypes.VIDEO:
        return { list: videos, name: 'videos' } as const;
        break;
      default:
        return { list: files, name: 'files' } as const;
    }
  };

  const handleDeleteAttachment = (attachment, index) => {
    const { name, list } = getAttachmentValue(attachment.type);

    const newList = [...list];
    newList.splice(index, 1);

    setValue(name, newList);
  };

  const getGroupedFiles = filesToGroup => {
    const multimedia = filesToGroup.reduce((acc, file) => {
      const { name, list } = getAttachmentValue(file.type);
      if (!acc[name]) {
        acc[name] = {
          list: list,
          files: [],
        };
      }
      acc[name].files.push(file);
      return acc;
    }, {});

    for (const multimediaName in multimedia) {
      const { list, files: multimediaFiles } = multimedia[multimediaName];
      const newList = [...list, ...multimediaFiles];
      setValue(multimediaName as 'images' | 'files' | 'videos', newList);
    }
  };

  const onAdd = async filesToAdd => {
    if (filesToAdd.length <= CHAT_CONSTANTS.MAX_FILES) {
      try {
        const validFiles = [];
        let rejectedCount = 0;

        filesToAdd.forEach(file => {
          if (fileSizeToMB(file.size) > MAX_SIZE_FILES) {
            rejectedCount++;
          } else {
            validFiles.push(file);
          }
        });

        if (rejectedCount > 0) {
          enqueueSnackbar({
            title: tChat('FILE_LIMIT_FEEDBACK', { count: rejectedCount }),
            variant: 'error',
          });
        }

        if (validFiles.length === 0) return;

        const processedFiles = await Promise.all(
          validFiles.map(file => processAttachment({ file })),
        );

        getGroupedFiles(processedFiles.filter(file => file !== null));
      } catch (err) {
        enqueueSnackbar({ title: t('ERROR_DRAG_AND_DROP'), variant: 'error' });
      }
    } else {
      enqueueSnackbar({
        title: t('web_only:form_inputs.upload_limit_multimedia'),
        variant: 'warning',
      });
    }
  };

  const hasAttachments =
    images.length > 0 || videos.length > 0 || files.length > 0;

  const totalFiles = images.length + videos.length + files.length;

  const isTicket = chatType === ChatType.TICKET;
  const imagesAttach = useMemo(() => images.map(getAttachment), [images]);
  const videosAttach = useMemo(() => videos.map(getAttachment), [videos]);
  const filesAttach = useMemo(() => files.map(getAttachment), [files]);

  const handleReplaceImage = (oldImage, newImage) => {
    const oldImageIndex = findImageIndex(images, oldImage);

    if (oldImageIndex !== -1) {
      const fileSizeInMB = fileSizeToMB(images[oldImageIndex].size);
      if (fileSizeInMB <= MAX_SIZE_FILES) {
        images[oldImageIndex] = newImage;
      }
    }
    setValue('images', [...images]);
  };

  return (
    <AudioProvider>
      {!inViewMessagesEnd && (
        <IconButton
          onClick={scrollToBottom}
          sx={{
            position: 'absolute',
            bottom: '10px',
            width: '42px',
            height: '42px',
            right: '22px',
            zIndex: '200',
            borderRadius: '50%',
            alignItems: 'center',
            justifyContent: 'center',
            backgroundColor: theme.palette.new.background.layout.tertiary,
            opacity: '1',
            '&.MuiButtonBase-root:hover': {
              bgcolor: theme.palette.new.background.layout.tertiary,
            },
          }}
        >
          <ExpandMore sx={{ color: theme.palette.new.text.neutral.lighter }} />
        </IconButton>
      )}
      <Container>
        <Box
          className="scrollbar"
          id="chat-window"
          style={{
            position: 'relative',
            height: '100%',
            overflowY: 'auto',
            overflowX: 'hidden',
          }}
        >
          <FormProvider {...form}>
            <form
              style={{ height: 'inherit' }}
              noValidate
            >
              <FormFileDropZoneChats
                onDrop={onAdd}
                disabled={disabledDragAndDrop(
                  chatStatus,
                  isResponsible,
                  allowAttachmentsInChats,
                  isTicket,
                )}
              >
                {isFetchingNextPage && (
                  <Box sx={{ textAlign: 'center' }}>
                    <CircularProgress color="primary" />
                  </Box>
                )}
                {hasNextPage && !!messages.length && (
                  <Box
                    id="LoadMoreRef"
                    sx={{
                      height: '10px',
                      backgroundColor: 'transparent',
                      position: 'absolute',
                      width: '100%',
                    }}
                    ref={loadMoreRef}
                  />
                )}
                <Box sx={{ p: 2 }}>
                  {messages.map((message, index) => {
                    const { sender, senderType } = getSender(
                      message.user,
                      message.userId,
                      isResponsible,
                      user,
                    );
                    const dateToShow = getDateToShow(
                      index,
                      message.createdAt,
                      messages[index - 1]?.createdAt,
                      user,
                    );

                    const attachment =
                      message.attachments?.length > 0 && message.attachments[0];

                    const indexRef = getMessageRef(
                      index,
                      message,
                      messageToScrollTo,
                      messageToScrollToRef,
                      lastMessageRef,
                    );

                    if (isInfo(message)) {
                      return (
                        <ChatInfo
                          ref={indexRef}
                          key={index}
                          body={message.text}
                        />
                      );
                    }

                    const showUnreadMessages =
                      unreadMessages !== 0 &&
                      index === messages.length - unreadMessages;

                    return (
                      <Fragment key={message.id}>
                        {showUnreadMessages && (
                          <Box ref={firstMessageUnreadRef}>
                            <ShowUnreadMessages
                              unreadMessages={unreadMessages}
                            />
                          </Box>
                        )}
                        <ChatMessage
                          ref={indexRef}
                          messageId={message.id}
                          chatId={message.chatId}
                          formData={formData}
                          key={index}
                          body={message.text}
                          createdAt={new Date(message.createdAt)}
                          contentType={message.type}
                          attachment={attachment}
                          senderType={senderType}
                          sender={sender}
                          senderId={message.userId}
                          chatType={chatType}
                          chatStatus={chatStatus}
                          topic={topic}
                          status={message.status}
                          dateToShow={dateToShow}
                          reactions={message.reactions}
                          isInTheChat={!message?.user?.hasLeftChat}
                          isResponsible={isResponsible}
                          replyMessage={message.replyMessage}
                        />
                      </Fragment>
                    );
                  })}
                  <AttachmentConfirmDialog
                    images={imagesAttach}
                    videos={videosAttach}
                    files={filesAttach}
                    isLoading={isLoading}
                    open={hasAttachments}
                    onDelete={handleDeleteAttachment}
                    onCancel={resetAttachments}
                    onAccept={handleSubmit(sendAttachment)}
                    onAddMore={filesToAdd => getGroupedFiles(filesToAdd)}
                    handleReplaceImage={handleReplaceImage}
                    addMoreDisabled={totalFiles >= CHAT_CONSTANTS.MAX_FILES}
                  />
                  {end}
                  <Box ref={setRef} />
                </Box>
              </FormFileDropZoneChats>
            </form>
          </FormProvider>
        </Box>
      </Container>
    </AudioProvider>
  );
};

export default ChatMessages;
