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

import { type LegendListRef } from '@legendapp/list/react';
import CircularProgress from '@material-hu/mui/CircularProgress';
import Stack from '@material-hu/mui/Stack';

import { GifPickerProvider } from '@material-hu/components/design-system/IconSelector';
import useSnackbar from '@material-hu/components/design-system/Snackbar';

import { useAuth } from 'src/contexts/JWTContext';
import { useLayoutContext } from 'src/contexts/LayoutContext';
import { usePageVisibilityContext } from 'src/contexts/PageVisibilityContext';
import useNetworkStatus from 'src/hooks/useNetworkStatus';
import {
  getIsSelfConversation,
  isNewerMessage,
  isValidUlid,
} from 'src/pages/dashboard/Conversations/utils';
import { useLokaliseTranslation } from 'src/utils/i18n';

import { videoCallKeys } from '../videoCall/queries';

import ConversationError from './components/Conversations/ConversationError';
import ConversationMessages from './components/ConversationsThread/ConversationMessages';
import GroupInfo from './components/ConversationsThread/ConversationsInfo/GroupInfo';
import ConversationsThreadFooter from './components/ConversationsThread/ConversationsThreadFooter';
import ConversationThreadToolbar from './components/ConversationsThread/ConversationThreadToolbar';
import { ReplyMessageConversationProvider } from './contexts/ReplayMessageConversationContext';
import { ScrollToMessageConversationProvider } from './contexts/ScrollToMessageConversationContext';
import { SearchHighlightProvider } from './contexts/SearchHighlightContext';
import { SendAttachmentsProvider } from './contexts/SendAttachmentsContext';
import {
  useGetActiveVideoCall,
  useGetConversationInfo,
  useGetConversationMembers,
  useGetMessages,
  useMarkMessages,
} from './hooks/useConversationsQueries';
import useGetMessagesEndRef from './hooks/useGetMessagesEndRef';
import {
  conversationKeys,
  getChannelFromList,
  resetUnreadMessages,
  resetUnreadMessagesInfo,
  updateLastRead,
} from './queries';

const ConversationThread: FC = () => {
  const [openGroupInfo, setOpenGroupInfo] = useState(false);
  const { user } = useAuth();
  const { collapseSidebar } = useLayoutContext();
  const { id } = useParams();
  const isValidId = isValidUlid(id);
  const { enqueueSnackbar } = useSnackbar();
  const { t } = useLokaliseTranslation('chat');
  const queryClient = useQueryClient();
  const markMessageMutation = useMarkMessages();
  const listRef = useRef<LegendListRef>(null);
  const {
    conversation,
    isError: isConversationError,
    isLoading: isLoadingConversationInfo,
    refetch: refetchConversation,
    isSuccess: isSuccessConversationInfo,
    isFetching: isFetchingConversationInfo,
  } = useGetConversationInfo({ id });

  const {
    members,
    isLoading: isLoadingMembers,
    refetch: refetchMembers,
    isError: isErrorMembers,
    isFetching: isFetchingMembers,
  } = useGetConversationMembers({ id });

  const { data: activeVideoCall, error: errorActiveVideoCall } =
    useGetActiveVideoCall({ id });

  const latestRead = conversation?.last_read;
  const isGroupConversation = conversation?.is_channel || false;
  const isSelfConversation = getIsSelfConversation(
    Number(conversation?.hu_data?.other_membership?.user?.id),
    user?.id,
    isGroupConversation,
  );

  const {
    messages,
    isLoading: isLoadingMessages,
    isError: isErrorMessages,
    fetchPreviousPage,
    hasPreviousPage,
    isFetchingPreviousPage,
    refetch: refetchMessages,
    isFetching: isFetchingMessages,
  } = useGetMessages({
    id,
    latestRead,
    enabled: isSuccessConversationInfo,
  });

  const {
    scrollToBottom,
    setRef: setFooterRef,
    inViewMessagesEnd,
  } = useGetMessagesEndRef(listRef);

  const { isOnline } = useNetworkStatus({
    onOnline: () => {
      enqueueSnackbar({
        title: t('connection.success'),
        variant: 'success',
      });
    },
    onOffline: () => {
      enqueueSnackbar({
        title: t('connection.error'),
        variant: 'error',
      });
    },
  });

  const isError =
    !isValidId || isErrorMessages || isConversationError || isErrorMembers;
  const isLoading =
    isLoadingMembers || isLoadingMessages || isLoadingConversationInfo;

  const isRefetching =
    (isErrorMembers && isFetchingMembers) ||
    (isConversationError && isFetchingConversationInfo) ||
    (isErrorMessages && isFetchingMessages);

  const refetch = useCallback(() => {
    if (isErrorMembers) refetchMembers();
    if (isConversationError) refetchConversation();
    if (isErrorMessages) refetchMessages();
  }, [
    isErrorMembers,
    isConversationError,
    isErrorMessages,
    refetchMembers,
    refetchConversation,
    refetchMessages,
  ]);

  const handleOpenGroupInfo = useCallback(() => {
    collapseSidebar();
    setOpenGroupInfo(true);
  }, [collapseSidebar]);

  const handleCloseGroupInfo = useCallback(() => setOpenGroupInfo(false), []);

  const handleMarkAsRead = useCallback(
    (options?: { resetInfo?: boolean; updateLastRead?: boolean }) => {
      if (!conversation) return;

      const {
        unread_count_display: unreadCountDisplay,
        properties: { marked_unread_at: markedUnreadAt },
        local_marked_unread_ts: localMarkedUnreadTs,
      } = conversation;

      const requireToMark =
        !localMarkedUnreadTs ||
        isNewerMessage({
          newTs: conversation?.latest?.ts,
          currentTs: localMarkedUnreadTs,
        });

      if (markedUnreadAt || (requireToMark && unreadCountDisplay > 0)) {
        markMessageMutation.mutate(
          {
            channelId: conversation.id,
            markedUnreadTs: conversation?.latest?.ts,
          },
          {
            onSuccess: (_, { channelId, markedUnreadTs }) => {
              if (markedUnreadTs) {
                resetUnreadMessages({ channelId, markedUnreadTs });
                if (options?.resetInfo) resetUnreadMessagesInfo({ channelId });
                if (options?.updateLastRead)
                  updateLastRead(channelId, markedUnreadTs);
              }
            },
          },
        );
      }
    },
    [conversation?.id, markMessageMutation],
  );

  const handleMarkAsReadOnLeave = useCallback(() => {
    if (!conversation) return;

    handleMarkAsRead({ resetInfo: true, updateLastRead: true });
    const conversationList = getChannelFromList(conversation.id);
    if (!conversationList) return;

    const { unread_count_display: unreadCountDisplayInfo } = conversation;
    const { unread_count_display: unreadCountDisplayList } = conversationList;
    const isLatestEqualLatestRead =
      conversation?.latest?.ts === conversation?.last_read;

    if (
      unreadCountDisplayList > 0 ||
      unreadCountDisplayInfo > 0 ||
      !isLatestEqualLatestRead
    ) {
      resetUnreadMessages({
        channelId: conversation.id,
        markedUnreadTs: conversation.latest.ts,
      });
      resetUnreadMessagesInfo({ channelId: conversation.id });
      updateLastRead(conversation.id, conversation.latest.ts);
    }
  }, [conversation?.id, handleMarkAsRead]);

  const { isVisibleRef } = usePageVisibilityContext(isVisible => {
    if (conversation) {
      if (isVisible) {
        handleMarkAsRead();
      } else {
        handleMarkAsReadOnLeave();
      }
    }
  });

  useEffect(() => {
    if (isVisibleRef.current && conversation) {
      handleMarkAsRead();
    }
    return () => {
      if (isVisibleRef.current && conversation) {
        handleMarkAsReadOnLeave();
      }
    };
  }, [conversation?.id]);

  useEffect(() => {
    if (!id) return;

    return () => {
      queryClient.cancelQueries(conversationKeys.info(id));
      queryClient.cancelQueries(conversationKeys.messages.all(id));
      queryClient.cancelQueries(conversationKeys.members.list(id, ''));
      queryClient.cancelQueries(videoCallKeys.activeVideoCall(id));
    };
  }, [id, queryClient]);

  return (
    <GifPickerProvider apiKey={import.meta.env.VITE_GIPHY_KEY ?? ''}>
      <ReplyMessageConversationProvider>
        <ScrollToMessageConversationProvider
          messages={messages}
          fetchNextPage={fetchPreviousPage}
          hasNextPage={hasPreviousPage || false}
          conversationId={id || ''}
          userId={user?.id}
          listRef={listRef}
        >
          <Stack
            key={id}
            sx={{
              width: '100%',
              height: '100%',
              minWidth: '300px',
            }}
          >
            {isLoading && (
              <Stack
                sx={{
                  alignItems: 'center',
                  justifyContent: 'center',
                  height: 'inherit',
                }}
              >
                <CircularProgress />
              </Stack>
            )}
            {!isLoading && isError && (
              <ConversationError
                refetch={refetch}
                isNetworkOnline={isOnline}
                isRefetching={isRefetching}
              />
            )}
            {!isLoading && !isError && conversation && (
              <SendAttachmentsProvider>
                <SearchHighlightProvider>
                  <ConversationThreadToolbar
                    onOpenGroupInfo={handleOpenGroupInfo}
                    members={members}
                    conversation={conversation}
                    activeVideoCall={
                      errorActiveVideoCall ? undefined : activeVideoCall
                    }
                  />
                  <ConversationMessages
                    messages={messages}
                    members={members}
                    listRef={listRef}
                    setFooterRef={setFooterRef}
                    isGroupConversation={isGroupConversation}
                    unreadCount={conversation?.unread_count_display || 0}
                    isFetchingPreviousPage={isFetchingPreviousPage || false}
                    hasPreviousPage={hasPreviousPage || false}
                    fetchPreviousPage={fetchPreviousPage}
                    latestRead={latestRead}
                    isSelfConversation={isSelfConversation}
                  />
                  <ConversationsThreadFooter
                    inViewMessagesEnd={inViewMessagesEnd}
                    onScrollToBottom={scrollToBottom}
                    isNetworkOnline={isOnline}
                    conversation={conversation}
                    isGroupConversation={isGroupConversation}
                    members={members}
                  />
                </SearchHighlightProvider>
              </SendAttachmentsProvider>
            )}
          </Stack>
          <GroupInfo
            members={members}
            conversation={conversation}
            openGroupInfo={openGroupInfo}
            onCloseGroupInfo={handleCloseGroupInfo}
          />
        </ScrollToMessageConversationProvider>
      </ReplyMessageConversationProvider>
    </GifPickerProvider>
  );
};

export default ConversationThread;
