import { InfiniteData } from 'react-query';

import { AxiosResponse } from 'axios';

import { queryClient } from 'src/config/react-query';
import { Comment } from 'src/types/comments';
import { DEFAULT_EMOJI } from 'src/types/feed';
import { Article, NewsNotificationsResponse } from 'src/types/news';
import { Updater } from 'src/types/queries';
import { Reaction } from 'src/types/reaction';
import { findPostIndexOnPages } from 'src/utils/feed';
import { findIndexOnPages } from 'src/utils/pages';
import { functionalUpdate } from 'src/utils/queries';
import {
  addReaction,
  removeReaction,
  sortReactions,
} from 'src/utils/reactions';

export const newsKeys = {
  all: () => ['articles'],
  list: () => [...newsKeys.all(), 'list'],
  listFilter: (topicId: number | null, q = '') => [
    ...newsKeys.list(),
    String(topicId ?? ''),
    q.trim(),
  ],
  allDetail: () => [...newsKeys.all(), 'detail'],
  detail: (id: number | string) => [...newsKeys.allDetail(), Number(id)],
  comment: {
    all: () => [...newsKeys.all(), 'comment'],
    list: () => [...newsKeys.comment.all(), 'list'],
    detail: (id: number | string) => [...newsKeys.comment.list(), Number(id)],
  },
  childComment: {
    all: () => [...newsKeys.all(), 'childComment'],
    list: () => [...newsKeys.childComment.all(), 'list'],
    detail: (id: number | string) => {
      return [...newsKeys.childComment.list(), Number(id)];
    },
  },
  notifications: {
    all: () => [...newsKeys.all(), 'notifications'],
    module: () => [...newsKeys.notifications.all(), 'module'],
  },
  topics: () => [...newsKeys.all(), 'topics'],
};

export const setArticleListData = (
  articleId: number,
  updater: Updater<Article>,
  filter: number | null,
  q = '',
) => {
  queryClient.setQueryData<InfiniteData<AxiosResponse<Article[]>> | undefined>(
    newsKeys.listFilter(filter, q),
    oldData => {
      if (!oldData) return oldData;

      const [pageIndex, articleIndex] = findPostIndexOnPages(
        articleId,
        oldData.pages,
      );

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

      return oldData;
    },
  );
};

export const setArticleDetailData = (
  articleId: number,
  updater: Updater<Article>,
) => {
  queryClient.setQueryData<AxiosResponse<Article> | undefined>(
    newsKeys.detail(articleId),
    oldData => {
      if (!oldData) return oldData;

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

      return oldData;
    },
  );
};

export const addReactionsInArticleDetail = (
  articleId: number,
  emoji: string,
  unified: string,
  isLoggedUserReaction: boolean = true,
) => {
  setArticleDetailData(articleId, (oldArticle?: Article) => {
    if (!oldArticle) return oldArticle;

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

    sortReactions(oldArticle.reactions, DEFAULT_EMOJI.emoji);

    return oldArticle;
  });
};
export const removeReactionsInArticleDetail = (
  articleId: number,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setArticleDetailData(articleId, oldArticle => {
    if (!oldArticle) return;

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

    sortReactions(oldArticle.reactions, DEFAULT_EMOJI.emoji);

    return oldArticle;
  });
};

export const addArticleListDataReaction = (
  filter: number | null,
  q: string,
  articleId: number,
  emoji: string,
  unified: string,
  isLoggedUserReaction: boolean = true,
) => {
  setArticleListData(
    articleId,
    oldArticle => {
      if (!oldArticle) return;

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

      sortReactions(oldArticle.reactions, DEFAULT_EMOJI.emoji);

      return oldArticle;
    },
    filter,
    q,
  );
};

export const removeArticleListDataReaction = (
  filter: number | null,
  q: string,
  articleId: number,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setArticleListData(
    articleId,
    oldArticle => {
      if (!oldArticle) return;

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

      sortReactions(oldArticle.reactions, DEFAULT_EMOJI.emoji);

      return oldArticle;
    },
    filter,
    q,
  );
};

// Notifications
export const addArticleNotification = (count: number = 1) => {
  queryClient.setQueryData<
    AxiosResponse<NewsNotificationsResponse> | undefined
  >(newsKeys.notifications.module(), oldData => {
    if (!oldData?.data) return oldData;

    oldData.data.articleNotifications += count;

    return oldData;
  });
};

export const removeArticleNotification = (count: number = 1) => {
  queryClient.setQueryData<
    AxiosResponse<NewsNotificationsResponse> | undefined
  >(newsKeys.notifications.module(), oldData => {
    if (!oldData?.data) return oldData;

    oldData.data.articleNotifications -= count;

    return oldData;
  });
};

export const clearNewsNotifications = () => {
  queryClient.setQueryData<
    AxiosResponse<NewsNotificationsResponse> | undefined
  >(newsKeys.notifications.module(), oldData => {
    if (!oldData?.data) return oldData;

    oldData.data.articleNotifications = 0;

    return oldData;
  });
};

// COMMENTS
export const increaseCommentAmount = (
  articleId: number,
  filter: number | null,
  q: string = '',
) => {
  setArticleListData(
    articleId,
    oldArticle => {
      if (!oldArticle) return;

      return { ...oldArticle, commentCount: oldArticle.commentCount + 1 };
    },
    filter,
    q,
  );
};

export const decreaseCommentAmount = (
  articleId: number,
  filter: number | null,
  q: string = '',
) => {
  setArticleListData(
    articleId,
    oldArticle => {
      if (!oldArticle) return;

      return {
        ...oldArticle,
        commentCount: Math.max(oldArticle.commentCount - 1, 0),
      };
    },
    filter,
    q,
  );
};

export const increaseCommentAmountArticleDetail = (articleId: number) => {
  setArticleDetailData(articleId, oldArticle => {
    if (!oldArticle) return;

    return { ...oldArticle, commentCount: oldArticle.commentCount + 1 };
  });
};

export const decreaseCommentAmountArticleDetail = (articleId: number) => {
  setArticleDetailData(articleId, oldArticle => {
    if (!oldArticle) return;

    return {
      ...oldArticle,
      commentCount: Math.max(oldArticle.commentCount - 1, 0),
    };
  });
};

export const setCommentListData = (
  articleId: number,
  commentId: number,
  updater: Updater<Comment>,
) => {
  queryClient.setQueryData<InfiniteData<AxiosResponse<Comment[]>> | undefined>(
    newsKeys.comment.detail(articleId),
    oldData => {
      if (!oldData) return oldData;

      // Handle infinite query structure (pages array)
      if (oldData.pages) {
        for (let pageIndex = 0; pageIndex < oldData.pages.length; pageIndex++) {
          const page = oldData.pages[pageIndex];
          if (page?.data) {
            const commentIndex = page.data.findIndex(
              (comment: Comment) => comment.id === commentId,
            );

            if (commentIndex !== -1) {
              page.data[commentIndex] = functionalUpdate(
                updater,
                page.data[commentIndex],
              );
              return oldData;
            }
          }
        }
      }

      return oldData;
    },
  );
};

export const updateCommentListData = (
  articleId: number,
  commentId: number,
  comment: Comment,
) => {
  setCommentListData(articleId, commentId, oldComment => {
    if (!oldComment) return oldComment;

    oldComment.body = comment.body;
    oldComment.bodyAttributes = comment.bodyAttributes;
    oldComment.attachments = comment.attachments;
    oldComment.updatedAt = comment.updatedAt;

    return oldComment;
  });
};

export const updateReactionCommentDataCommentList = (
  articleId: number,
  commentId: number,
  updater: Updater<Reaction[]>,
) => {
  setCommentListData(articleId, commentId, oldComment => {
    if (!oldComment) return oldComment;

    oldComment.reactions = functionalUpdate(updater, oldComment.reactions);

    sortReactions(oldComment.reactions, DEFAULT_EMOJI.emoji);

    return oldComment;
  });
};

export const pushCommentDataCommentList = (
  articleId: number,
  comment: Comment,
) => {
  queryClient.setQueryData<InfiniteData<AxiosResponse<Comment[]>> | undefined>(
    newsKeys.comment.detail(articleId),
    oldData => {
      if (!oldData) return oldData;

      // Handle infinite query structure (pages array)
      if (oldData.pages) {
        // Add to the first page
        if (oldData.pages[0]?.data) {
          oldData.pages[0].data = [comment, ...oldData.pages[0].data];
        }
      }

      return oldData;
    },
  );
};

export const dropCommentDataCommentList = (
  articleId: number,
  commentId: number,
) => {
  queryClient.setQueryData<InfiniteData<AxiosResponse<Comment[]>> | undefined>(
    newsKeys.comment.detail(articleId),
    oldData => {
      if (!oldData) return oldData;

      // Handle infinite query structure (pages array)
      if (oldData.pages) {
        for (let pageIndex = 0; pageIndex < oldData.pages.length; pageIndex++) {
          const page = oldData.pages[pageIndex];
          if (page?.data) {
            const commentIndex = page.data.findIndex(
              (comment: Comment) => comment.id === commentId,
            );

            if (commentIndex !== -1) {
              // Create new array without the comment
              const newData = page.data.filter(
                comment => comment.id !== commentId,
              );
              // Return a completely new object to trigger re-render
              return {
                ...oldData,
                pages: oldData.pages.map((p, idx) =>
                  idx === pageIndex ? { ...p, data: newData } : p,
                ),
              };
            }
          }
        }
      }

      return oldData;
    },
  );
};

export const setChildCommentDataCommentList = (
  articleId: number,
  commentId: number,
  childId: number,
  updater: Updater<Comment>,
) => {
  setCommentListData(articleId, commentId, oldComment => {
    if (!oldComment || !oldComment.children) return oldComment;

    const childIndex = oldComment.children.findIndex(
      child => child.id === childId,
    );

    if (childIndex !== -1) {
      oldComment.children[childIndex] = functionalUpdate(
        updater,
        oldComment.children[childIndex],
      );
    }

    return oldComment;
  });
};

export const setChildCommentDataChildCommentList = (
  commentId: number,
  childId: number,
  updater: Updater<Comment>,
) => {
  queryClient.setQueryData<InfiniteData<AxiosResponse<Comment[]>> | undefined>(
    newsKeys.childComment.detail(commentId),
    oldData => {
      if (!oldData) return oldData;

      // Handle infinite query structure (pages array)
      if (oldData.pages) {
        const [pageIndex, childIndex] = findIndexOnPages<Comment>(
          oldData.pages,
          child => child.id === childId,
        );

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

      return oldData;
    },
  );
};

export const updateReactionChildCommentDataCommentList = (
  articleId: number,
  commentId: number,
  childId: number,
  updater: Updater<Reaction[]>,
) => {
  setChildCommentDataCommentList(articleId, commentId, childId, oldChild => {
    if (!oldChild) return oldChild;

    oldChild.reactions = functionalUpdate(updater, oldChild.reactions);

    sortReactions(oldChild.reactions, DEFAULT_EMOJI.emoji);

    return oldChild;
  });
};

export const updateReactionChildCommentDataChildCommentList = (
  commentId: number,
  childId: number,
  updater: Updater<Reaction[]>,
) => {
  setChildCommentDataChildCommentList(commentId, childId, oldChild => {
    if (!oldChild) return oldChild;

    oldChild.reactions = functionalUpdate(updater, oldChild.reactions);

    sortReactions(oldChild.reactions, DEFAULT_EMOJI.emoji);

    return oldChild;
  });
};

export const dropChildCommentDataCommentList = (
  articleId: number,
  commentId: number,
  childId: number,
) => {
  setCommentListData(articleId, commentId, oldComment => {
    if (!oldComment || !oldComment.children) return oldComment;

    const childIndex = oldComment.children.findIndex(
      child => child.id === childId,
    );

    if (childIndex !== -1) {
      oldComment.children.splice(childIndex, 1);
    }

    return oldComment;
  });
};

export const dropChildCommentDataChildCommentList = (
  commentId: number,
  childId: number,
) => {
  queryClient.setQueryData<InfiniteData<AxiosResponse<Comment[]>> | undefined>(
    newsKeys.childComment.detail(commentId),
    oldData => {
      if (!oldData) return oldData;

      // Handle infinite query structure (pages array)
      if (oldData.pages) {
        const [pageIndex, childIndex] = findIndexOnPages<Comment>(
          oldData.pages,
          child => child.id === childId,
        );

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

      return oldData;
    },
  );
};
