import { InfiniteData, QueryKey } from 'react-query';

import { AxiosResponse } from 'axios';

import { queryClient } from 'src/config/react-query';
import {
  ChatListItem,
  TicketChatListItem,
  ChatByIdResponse,
  ChatMessagesResponse,
  Message,
  ChatListResponsePagination,
} from 'src/types/chats';
import { Updater } from 'src/types/queries';
import { updateMessage } from 'src/utils/chats';
import { findIndexOnPaginatedPages } from 'src/utils/pages';
import { functionalUpdate } from 'src/utils/queries';
import {
  addReaction,
  removeReaction,
  sortReactions,
} from 'src/utils/reactions';

import { chatKeys } from 'src/components/dashboard/chat/queries';

type ListData = InfiniteData<AxiosResponse<ChatListResponsePagination>>;

export const ticketKeys = {
  all: () => ['tickets'],
  list: {
    all: () => [...ticketKeys.all(), 'list'],
    unassigned: () => [...ticketKeys.list.all(), 'unassigned'],
    assigned: () => [...ticketKeys.list.all(), 'assigned'],
    closed: () => [...ticketKeys.list.all(), 'closed'],
  },
  allDetail: () => [...ticketKeys.all(), 'detail'],
  detail: (id: number | string) => [...ticketKeys.allDetail(), Number(id)],
  topic: {
    all: () => [...ticketKeys.all(), 'topic'],
    list: () => [...ticketKeys.topic.all(), 'list'],
  },
  messages: {
    all: () => [...ticketKeys.all(), 'messages'],
    list: (id: number | string) => [...ticketKeys.messages.all(), Number(id)],
  },
};

export const pushTicketListData = (ticketItem: ChatListItem) => {
  queryClient.setQueryData(
    ticketKeys.list.unassigned(),
    (oldData: ListData) => {
      if (!oldData?.pages?.length) return oldData;
      if (!ticketItem) return oldData;

      oldData.pages[0].data.items = [
        ticketItem,
        ...oldData.pages[0].data.items,
      ];

      oldData.pages[0].data.count++;

      return oldData;
    },
  );
};

export const dropTicketListData = (ticketItemId: number) => {
  queryClient.setQueriesData(ticketKeys.list.all(), (oldData: ListData) => {
    if (!oldData?.pages?.length) return oldData;
    if (!ticketItemId) return oldData;

    const [pageIndex, ticketIndex] = findIndexOnPaginatedPages<ChatListItem>(
      oldData.pages,
      item => item.chat.id === ticketItemId,
    );

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

    return oldData;
  });
};

export const moveFirstTicketListData = (
  ticketItemId: number,
  queryKey: any,
  numberPage = 0,
) => {
  queryClient.setQueryData(queryKey, (oldData: ListData) => {
    if (!oldData) return oldData;

    const ticketItem = oldData.pages[numberPage].data.items.find(
      item => item.chat.id === ticketItemId,
    );

    if (ticketItem) {
      oldData.pages[numberPage].data.items = [
        ticketItem,
        ...oldData.pages[numberPage].data.items.filter(
          item => item.chat.id !== ticketItem.chat.id,
        ),
      ];
    } else {
      queryClient.invalidateQueries(queryKey);
    }

    return oldData;
  });
};

export const isTicket = (id: number): boolean => {
  const ticketListData: [QueryKey, ListData][] = queryClient.getQueriesData(
    ticketKeys.list.all(),
  );

  return ticketListData.some(
    ([, data]) =>
      !!data?.pages?.some(
        page => !!page?.data?.items?.some(item => item.chat.id === id),
      ),
  );
};

export const getUnreadTickets = (): number[] => {
  const ticketListData: ListData = queryClient.getQueryData(
    ticketKeys.list.assigned(),
  );

  const unreadTickets = [];

  ticketListData?.pages?.forEach(page =>
    page?.data?.items?.forEach(item => {
      if (item?.chat?.unreadMessages > 0) {
        unreadTickets.push(item.chat.id);
      }
    }),
  );

  return unreadTickets;
};

export const isTicketOfTopic = (
  ticketItemId: number,
  topicId: number,
): boolean => {
  const ticket = getTicket(ticketItemId);

  if (!ticket?.topic) return false;

  return ticket.topic.id === topicId;
};

export const getTicket = (id: number): TicketChatListItem => {
  const ticketData: AxiosResponse<TicketChatListItem> =
    queryClient.getQueryData(ticketKeys.detail(id));

  return ticketData?.data;
};

export const setTicketListData = (
  ticketItemId: number,
  queryKey: any,
  updater: Updater<ChatListItem>,
) => {
  queryClient.setQueryData(queryKey, (oldData: any) => {
    if (!oldData?.pages.length) return oldData;

    const [pageIndex, ticketIndex] = findIndexOnPaginatedPages<ChatListItem>(
      oldData.pages,
      item => item.chat.id === ticketItemId,
    );

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

    return oldData;
  });
};

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

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

      return oldData;
    },
  );
};

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

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

      return oldData;
    },
  );
};

export const setTicketDetailMessageData = (
  ticketItemId: number,
  messageId: number,
  updater: Updater<Message>,
) => {
  queryClient.setQueryData(
    ticketKeys.messages.list(ticketItemId),
    (oldData: any) => {
      if (!oldData) return oldData;

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

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

      return oldData;
    },
  );
};

export const addTicketMessageDataReaction = (
  ticketItemId: number,
  messageId: string | number,
  emoji: string,
  unified,
  isLoggedUserReaction: boolean = true,
) => {
  setTicketDetailMessageData(ticketItemId, Number(messageId), oldMessage => {
    if (!oldMessage) return oldMessage;

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

    sortReactions(oldMessage.reactions);

    return oldMessage;
  });
};

export const removeTicketMessageDataReaction = (
  ticketItemId: number,
  messageId: string | number,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setTicketDetailMessageData(ticketItemId, Number(messageId), oldMessage => {
    if (!oldMessage) return oldMessage;

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

    sortReactions(oldMessage.reactions);

    return oldMessage;
  });
};

// Notifications

export const addTicketNotification = (userId: number, count: number = 1) => {
  queryClient.setQueryData(
    chatKeys.ticketAgentStats.detail(userId),
    (oldData: AxiosResponse<any>) => {
      if (!oldData?.data) return oldData;

      oldData.data.amountOfTicketsWhichNeedAttention += count;

      return oldData;
    },
  );
};

export const removeTicketNotification = (userId: number, count: number = 1) => {
  queryClient.setQueryData(
    chatKeys.ticketAgentStats.detail(userId),
    (oldData: AxiosResponse<any>) => {
      if (!oldData?.data) return oldData;

      oldData.data.amountOfTicketsWhichNeedAttention -= count;

      return oldData;
    },
  );
};

export const addTicketDetailUnreadMessage = (id: number, count: number = 1) => {
  setTicketDetailData(id, (chatItem: ChatListItem) => {
    if (!chatItem?.chat) return chatItem;

    chatItem.chat.unreadMessages += count;

    return chatItem;
  });
};

export const clearTicketUnreadMessages = (id: number) => {
  setTicketListData(
    id,
    ticketKeys.list.assigned(),
    (chatItem: ChatListItem) => {
      if (!chatItem?.chat) return chatItem;

      chatItem.chat.unreadMessages = 0;

      return chatItem;
    },
  );
  setTicketListData(
    id,
    ticketKeys.list.unassigned(),
    (chatItem: ChatListItem) => {
      if (!chatItem?.chat) return chatItem;

      chatItem.chat.unreadMessages = 0;

      return chatItem;
    },
  );
};

export const clearTicketDetailUnreadMessages = (id: number) => {
  setTicketDetailData(id, (chatItem: ChatListItem) => {
    if (!chatItem?.chat) return chatItem;

    chatItem.chat.unreadMessages = 0;

    return chatItem;
  });
};

export const clearTicketNotifications = (ticketId: number, userId: number) => {
  removeTicketNotification(userId);
  clearTicketUnreadMessages(ticketId);
};

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

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

      return oldData;
    },
  );
};

export const editTicketMessage = data => {
  const { id, chatId } = data;

  forEachTicketDetailMessage(Number(chatId), oldMessage => {
    if (!oldMessage) return oldMessage;

    if (oldMessage.id === id) {
      updateMessage(oldMessage, data);
    } else if (oldMessage.replyMessageId === id) {
      updateMessage(oldMessage.replyMessage, data);
    }

    return oldMessage;
  });
};

export const updateAgentTickets = () => {
  queryClient.invalidateQueries(ticketKeys.list.all());
  queryClient.invalidateQueries(chatKeys.ticketAgentStats.all());
};

export const ticketInFirstPage = (
  chatId: number,
  queryKey: QueryKey,
): boolean => {
  const ticketListData: ListData = queryClient.getQueryData(queryKey);

  if (!ticketListData?.pages?.length) return false;

  return !!ticketListData.pages[0]?.data?.items?.some(
    item => item.chat.id === chatId,
  );
};

export const updateTransitionTicket = (id, user, queryKey) => {
  queryClient.invalidateQueries(queryKey);
  queryClient.invalidateQueries(ticketKeys.detail(id));
  queryClient.invalidateQueries(ticketKeys.list.assigned());
  queryClient.invalidateQueries(chatKeys.ticketAgentStats.detail(user.id));
};
