import { InfiniteData } from 'react-query';

import { AxiosResponse } from 'axios';

import { queryClient } from 'src/config/react-query';
import { ticketKeys } from 'src/pages/dashboard/tickets/queries';
import {
  ChatListItem,
  Message,
  ChatListResponse,
  PaginatedChatResponse,
  ChatByIdResponse,
  TicketAgentStats,
  ChatMessagesResponse,
  ChatMessagesQueryData,
  ChatsNotificationsResponse,
  ChatType,
} from 'src/types/chats';
import { Updater } from 'src/types/queries';
import { findIndexOnPaginatedPages, findIndexOnPages } from 'src/utils/pages';
import { functionalUpdate } from 'src/utils/queries';
import {
  addReaction,
  removeReaction,
  sortReactions,
} from 'src/utils/reactions';

import { formKeys } from 'src/components/dashboard/form/queries';

export const chatKeys = {
  all: () => ['chats'],
  list: (name = '') => [...chatKeys.all(), name, 'list'],
  allDetail: () => [...chatKeys.all(), 'detail'],
  detail: (id: number | string) => [...chatKeys.allDetail(), Number(id)],
  allParticipants: () => [...chatKeys.all(), 'participants'],
  participants: (id: number | string) => [
    ...chatKeys.allParticipants(),
    Number(id),
  ],
  allChatables: (query: string) => [...chatKeys.all(), 'allChatables', query],
  archivedChats: {
    all: () => [...chatKeys.all(), 'archivedChats'],
    list: (name = '') => [...chatKeys.archivedChats.all(), name, 'list'],
  },
  ticketAgentStats: {
    all: () => [...chatKeys.all(), 'ticketAgentStats'],
    detail: (id: number | string) => [
      ...chatKeys.ticketAgentStats.all(),
      Number(id),
    ],
  },
  messages: {
    all: () => [...chatKeys.all(), 'messages'],
    list: (id: number | string) => [...chatKeys.messages.all(), Number(id)],
  },
  notifications: {
    all: () => [...chatKeys.all(), 'notifications'],
    module: () => [...chatKeys.notifications.all(), 'module'],
  },
};

export const deleteChatOfCache = id => {
  queryClient.removeQueries(chatKeys.messages.list(id), { exact: true });
};

export const removeTicketAgentStat = (userId: number) => {
  queryClient.setQueryData(
    chatKeys.ticketAgentStats.detail(userId),
    (oldData: AxiosResponse<TicketAgentStats>) => {
      if (!oldData) return oldData;

      oldData.data.amountOfTicketsWhichNeedAttention--;

      return oldData;
    },
  );
};

export const getUnreadChats = (): number[] => {
  const chatListData: InfiniteData<AxiosResponse<ChatListResponse>> =
    queryClient.getQueryData(chatKeys.list());

  if (!chatListData?.pages?.length) return [];

  const listChatsUnread = chatListData.pages
    .flatMap(chatList => chatList.data)
    .filter(chatItem => chatItem?.chat?.unreadMessages > 0)
    .map(elem => elem.chat.id);

  return listChatsUnread;
};

export const getMessage = (
  chatId: string | number,
  messageId: string | number,
  chatType: string,
  isChatMessage: boolean,
): Message | undefined => {
  let queryKey;
  if (chatType === ChatType.FORM && !isChatMessage) {
    queryKey = formKeys.messages.list(chatId);
  } else if (chatType === ChatType.TICKET && !isChat(Number(chatId))) {
    queryKey = ticketKeys.messages.list(chatId);
  } else {
    queryKey = chatKeys.messages.list(chatId);
  }
  const messageListData: ChatMessagesQueryData =
    queryClient.getQueryData(queryKey);

  if (!messageListData?.pages?.length) return undefined;

  const message = messageListData.pages
    .flatMap(messageList => messageList?.data?.items)
    .find(messageItem => Number(messageItem?.id) === Number(messageId));

  return message;
};

export const refetchListChats = () => {
  queryClient.invalidateQueries(chatKeys.list());
};

export const refetchArchiveListChats = () => {
  queryClient.invalidateQueries(chatKeys.archivedChats.list());
};

export const chatInFirstPage = (chatId: number) => {
  const formPageListData: InfiniteData<AxiosResponse<ChatListResponse>> =
    queryClient.getQueryData(chatKeys.list());

  const formPage = formPageListData?.pages.findIndex(element =>
    element.data.find(item => item.chat.id === chatId),
  );

  return formPage === 0;
};

export const dropChatListData = (chatItemId: number) => {
  queryClient.setQueryData(
    chatKeys.list(),
    (oldData: AxiosResponse<ChatListResponse>) => {
      if (!oldData) return oldData;

      oldData.data = [
        ...oldData.data.filter(item => item.chat.id !== chatItemId),
      ];

      return oldData;
    },
  );
};

export const moveFirstChatListData = (chatItemId: number, numberPage = 0) => {
  queryClient.setQueryData(
    chatKeys.list(),
    (oldData: InfiniteData<AxiosResponse<ChatListResponse>>) => {
      if (!oldData) return oldData;

      const chatItem = oldData.pages[numberPage].data.find(
        item => item.chat.id === chatItemId,
      );

      if (chatItem) {
        oldData.pages[numberPage].data = [
          chatItem,
          ...oldData.pages[numberPage].data.filter(
            item => item.chat.id !== chatItem.chat.id,
          ),
        ];
      }

      return oldData;
    },
  );
};

export const isChat = (id: number): boolean => {
  const chatListData: InfiniteData<AxiosResponse<ChatListResponse>> =
    queryClient.getQueryData(chatKeys.list());

  if (!chatListData?.pages) return false;

  const [pageIndex, chatIndex] = findIndexOnPages(
    chatListData?.pages,
    item => item.chat.id === id,
  );

  return pageIndex !== -1 && chatIndex !== -1;
};

export const setChatListData = (
  chatItemId: number,
  updater: Updater<ChatListItem>,
) => {
  queryClient.setQueryData(
    chatKeys.list(),
    (oldData: InfiniteData<AxiosResponse<ChatListResponse>>) => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, chatIndex] = findIndexOnPages(
        oldData?.pages,
        item => item.chat.id === chatItemId,
      );

      if (pageIndex !== -1 && chatIndex !== -1) {
        oldData.pages[pageIndex].data[chatIndex] = functionalUpdate(
          updater,
          oldData.pages[pageIndex].data[chatIndex],
        );
      }

      return oldData;
    },
  );
};

export const setChatArchiveListData = (
  chatItemId: number,
  updater: Updater<ChatListItem>,
) => {
  queryClient.setQueryData(
    chatKeys.archivedChats.list(),
    (oldData: InfiniteData<AxiosResponse<PaginatedChatResponse>>) => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, chatIndex] = findIndexOnPaginatedPages(
        oldData?.pages,
        item => item.chat.id === chatItemId,
      );

      if (pageIndex !== -1 && chatIndex !== -1) {
        oldData.pages[pageIndex].data.items[chatIndex] = functionalUpdate(
          updater,
          oldData.pages[pageIndex].data.items[chatIndex],
        );
      }

      return oldData;
    },
  );
};

export const setChatDetailData = (
  chatItemId: number,
  updater: Updater<ChatListItem>,
) => {
  queryClient.setQueryData(
    chatKeys.detail(chatItemId),
    (oldData: AxiosResponse<ChatByIdResponse>) => {
      if (!oldData) return oldData;

      oldData.data = functionalUpdate(updater, oldData.data);

      return oldData;
    },
  );
};

export const setMessageListData = (
  chatItemId: number,
  updater: Updater<ChatMessagesResponse>,
  pageNumber: number = 0,
) => {
  queryClient.setQueryData(
    chatKeys.messages.list(chatItemId),
    (oldData: any) => {
      if (!oldData) return oldData;

      oldData.pages[pageNumber].data = functionalUpdate(
        updater,
        oldData.pages[pageNumber].data,
      );

      return oldData;
    },
  );
};

export const setChatDetailMessage = (
  chatItemId: number,
  messageId: string | number,
  updater: Updater<Message>,
) => {
  queryClient.setQueryData(
    chatKeys.messages.list(chatItemId),
    (oldData: any) => {
      if (!oldData) return oldData;

      const [pageIndex, messageIndex] = findIndexOnPaginatedPages<Message>(
        oldData.pages,
        item => item.id === messageId,
      );

      if (pageIndex !== -1 && messageIndex !== -1) {
        oldData.pages[pageIndex].data.items[messageIndex] = functionalUpdate(
          updater,
          oldData.pages[pageIndex].data.items[messageIndex],
        );
      }

      return oldData;
    },
  );
};

export const pushChatDetailMessage = (
  chatItemId: number,
  newMessage: Message,
) => {
  setMessageListData(chatItemId, oldData => {
    if (!oldData) return oldData;

    oldData.items = [newMessage, ...(oldData.items || [])];

    return oldData;
  });
};

export const replaceChatDetailMessage = (
  chatItemId: number,
  messageId: string | number,
  newMessage: Message,
) => {
  setChatDetailMessage(chatItemId, messageId, oldData => {
    if (!oldData) return oldData;

    oldData = newMessage;

    return oldData;
  });
};

export const addChatMessageDataReaction = (
  chatItemId: number,
  messageId: string | number,
  emoji: string,
  unified: string,
  isLoggedUserReaction: boolean = true,
) => {
  setChatDetailMessage(chatItemId, messageId, oldMessage => {
    if (!oldMessage) return oldMessage;

    oldMessage.reactions = addReaction(
      oldMessage.reactions,
      emoji,
      unified,
      isLoggedUserReaction,
    );

    sortReactions(oldMessage.reactions);

    return oldMessage;
  });
};

export const removeChatMessageDataReaction = (
  chatItemId: number,
  messageId: string | number,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setChatDetailMessage(chatItemId, messageId, oldMessage => {
    if (!oldMessage) return oldMessage;

    oldMessage.reactions = removeReaction(
      oldMessage.reactions,
      emoji,
      isLoggedUserReaction,
    );

    sortReactions(oldMessage.reactions);

    return oldMessage;
  });
};

export const forEachChatDetailMessage = (
  chatItemId: number,
  updater: Updater<Message>,
) => {
  queryClient.setQueryData(
    chatKeys.messages.list(chatItemId),
    (oldData: any) => {
      if (!oldData) return oldData;

      oldData?.pages?.forEach(page =>
        page?.data?.items?.forEach(message =>
          functionalUpdate(updater, message),
        ),
      );

      return oldData;
    },
  );
};

// Notifications

const clearUnreadUpdater = (chatItem: ChatListItem) => {
  if (!chatItem?.chat) return chatItem;

  chatItem.chat.markedAsUnread = false;
  chatItem.chat.unreadMessages = 0;

  return chatItem;
};

export const clearUnreadMessages = (id: number) => {
  setChatListData(id, clearUnreadUpdater);
};

export const clearArchiveUnreadMessages = (id: number) => {
  setChatArchiveListData(id, clearUnreadUpdater);
};

export const clearDetailUnreadMessages = (id: number) => {
  setChatDetailData(id, clearUnreadUpdater);
};

export const addChatNotification = (count: number = 1) => {
  queryClient.setQueryData(
    chatKeys.notifications.module(),
    (oldData: AxiosResponse<ChatsNotificationsResponse>) => {
      if (!oldData?.data) return oldData;

      oldData.data.userChatNotifications += count;

      return oldData;
    },
  );
};

export const removeChatNotification = (count: number = 1) => {
  queryClient.setQueryData(
    chatKeys.notifications.module(),
    (oldData: AxiosResponse<ChatsNotificationsResponse>) => {
      if (!oldData?.data) return oldData;

      oldData.data.userChatNotifications -= count;

      return oldData;
    },
  );
};

export const clearChatNotifications = (id: number) => {
  removeChatNotification();
  clearUnreadMessages(id);
  clearDetailUnreadMessages(id);
};

export const updateAdmins = data => {
  queryClient.invalidateQueries(chatKeys.participants(data.chatId));
};
