import { InfiniteData } from 'react-query';

import { AxiosResponse } from 'axios';

import { sharedPostKeys } from 'src/components/post/SharedPost/keys';
import { queryClient } from 'src/config/react-query';
import { Comment } from 'src/types/comments';
import { DEFAULT_EMOJI } from 'src/types/feed';
import {
  Group,
  GroupCommentList,
  GroupPost,
  GroupPostList,
} from 'src/types/groups';
import { InsightsTypes, Post } from 'src/types/posts';
import { DataUpdate, Updater } from 'src/types/queries';
import { Reaction } from 'src/types/reaction';
import { Pagination } from 'src/types/services';
import { AudienceTypes } from 'src/types/stream';
import {
  decreaseCommentCount,
  includesComment,
  lastCommentUpdater,
  unshiftChildUpdater,
  unshiftCommentUpdater,
} from 'src/utils/comments';
import { addVote } from 'src/utils/feed';
import { findPostIndexOnGroupPages } from 'src/utils/groups';
import { functionalUpdate } from 'src/utils/queries';
import {
  addReaction,
  removeReaction,
  sortReactions,
} from 'src/utils/reactions';

import {
  feedKeys,
  setFeedListData,
  setKeyUpdatesListData,
  setLastCommentDataAllSearchList,
  setLastCommentDataFeedList,
  setLastCommentDataKeyUpdatesList,
  setLastCommentDataPostDetail,
} from '../feed/queries';

export const groupsMutationKeys = {
  all: () => ['groups', 'mutation'] as const,
  join: () => [...groupsMutationKeys.all(), 'join'] as const,
  pin: () => [...groupsMutationKeys.all(), 'pin'] as const,
  unpin: () => [...groupsMutationKeys.all(), 'unpin'] as const,
  create: () => [...groupsMutationKeys.all(), 'create'] as const,
};

export const groupsKeys = {
  all: () => ['groups'],
  allList: () => [...groupsKeys.all(), 'list'],
  allPinnedList: () => [...groupsKeys.allList(), 'pinned'],
  allUnpinnedList: () => [...groupsKeys.allList(), 'unpinned'],
  list: (groupName: string) => [...groupsKeys.allList(), groupName],
  allMyGroups: () => [...groupsKeys.all(), 'myGroups'],
  myGroups: (groupName: string) => [...groupsKeys.allMyGroups(), groupName],
  pinnedGroups: (groupName: string) => [
    ...groupsKeys.allPinnedList(),
    groupName,
  ],
  allExploreGroups: () => [...groupsKeys.all(), 'exploreGroups'],
  exploreGroups: (groupName: string) => [
    ...groupsKeys.allExploreGroups(),
    groupName,
  ],
  detail: (groupId: string) => [...groupsKeys.all(), 'detail', groupId],
  postList: (groupId: string) => [...groupsKeys.all(), 'postList', groupId],
  scheduledPostList: (groupId: string) => [
    ...groupsKeys.all(),
    'scheduledPostList',
    groupId,
  ],
  postPinList: (groupId: string) => [
    ...groupsKeys.all(),
    'postPinList',
    groupId,
  ],
  postDetail: (groupId: string, postId: string) => [
    ...groupsKeys.all(),
    'postDetail',
    groupId,
    postId,
  ],
  searchAllPost: (groupId: string) => [...groupsKeys.all(), 'search', groupId],
  searchPost: (groupId: string, query: string) => [
    ...groupsKeys.searchAllPost(groupId),
    query,
  ],
  allRequests: (groupId: string) => [...groupsKeys.all(), 'request', groupId],
  requests: (groupId: string, params: object) => [
    ...groupsKeys.allRequests(groupId),
    params,
  ],
  searchAllMembers: (groupId: string) => [
    ...groupsKeys.all(),
    'members',
    groupId,
  ],
  searchMember: (groupId: string, query: string) => [
    ...groupsKeys.searchAllMembers(groupId),
    query,
  ],
  searchManageMember: (groupId: string, params: object) => [
    ...groupsKeys.searchAllMembers(groupId),
    'manage',
    params,
  ],
  comment: {
    all: () => [...groupsKeys.all(), 'comment'],
    list: (groupId: string) => [...groupsKeys.comment.all(), 'list', groupId],
    detail: (postId: number, groupId: string) => [
      ...groupsKeys.comment.list(groupId),
      postId,
    ],
    liveStream: (postId: number, groupId: string) => [
      ...groupsKeys.comment.list(groupId),
      'liveStream',
      postId,
    ],
  },
  childComment: {
    all: () => [...groupsKeys.all(), 'childComment'],
    list: (groupId: string) => [
      ...groupsKeys.childComment.all(),
      'list',
      groupId,
    ],
    detail: (commentId: number, groupId: string) => [
      ...groupsKeys.childComment.list(groupId),
      commentId,
    ],
  },
  notifications: {
    all: () => [...groupsKeys.all(), 'notifications'],
    notificationRedBubblesCount: () => [
      ...groupsKeys.notifications.all(),
      'notificationCount',
    ],
    mailNotificationSettings: (groupId: string) => [
      ...groupsKeys.notifications.all(),
      'mailNotificationSettings',
      groupId,
    ],
  },
  assignableMembers: (groupId: string, params: { search: string }) => [
    ...groupsKeys.all(),
    'assignableMembers',
    groupId,
    params,
  ],
  translate: {
    all: () => [...groupsKeys.all(), 'translate'],
    detail: (groupId: string, postId: number | string) => [
      ...groupsKeys.translate.all(),
      'detail',
      groupId,
      postId,
    ],
  },
  pendingApproval: {
    all: () => [...groupsKeys.all(), 'pendingApproval'],
    postList: (groupId: string, params = {}) => [
      ...groupsKeys.pendingApproval.all(),
      'postList',
      groupId,
      params,
    ],
  },
  insights: {
    allDetail: () => [...groupsKeys.all(), 'insights'],
    postDetail: (groupId: string, postId: string) => [
      ...groupsKeys.insights.allDetail(),
      'postDetail',
      groupId,
      postId,
    ],
    summary: (groupId: string, postId: string) => [
      ...groupsKeys.insights.postDetail(groupId, postId),
      'summary',
    ],
    audience: (
      groupId: string,
      postId: string,
      type: InsightsTypes,
      query: string,
    ) => [
      ...groupsKeys.insights.postDetail(groupId, postId),
      type,
      'audience',
      query,
    ],
    liveStreamTimeline: (groupId: string, postId: string) => [
      ...groupsKeys.insights.postDetail(groupId, postId),
      'liveStreamTimeline',
    ],
    liveStreamStats: (groupId: string, postId: string) => [
      ...groupsKeys.insights.postDetail(groupId, postId),
      'liveStraemStats',
    ],
    liveStreamViewerReports: (
      groupId: string,
      postId: string,
      type: AudienceTypes,
      query = '',
    ) => [
      ...groupsKeys.insights.postDetail(groupId, postId),
      'liveStreamViewerReports',
      type,
      query,
    ],
  },
  groupMembersReport: (groupId: string, email: string) => [
    ...groupsKeys.all(),
    groupId,
    'members',
    'report',
    email,
  ],
  keyUpdates: {
    unread: (groupId: string) => [...groupsKeys.all(), 'keyUpdates', groupId],
  },
  featuredMembers: {
    all: () => [...groupsKeys.all(), 'stats'],
    summary: (groupId: string) => [
      ...groupsKeys.featuredMembers.all(),
      'summary',
      groupId,
    ],
    activeMembers: (groupId: string) => [
      ...groupsKeys.featuredMembers.all(),
      'activeMembers',
      groupId,
    ],
    activeMembersReport: (groupId: string, email: string) => [
      ...groupsKeys.featuredMembers.all(),
      'activeMembersReport',
      groupId,
      email,
    ],
  },
  multiCompany: {
    list: (groupId: string) => ['multiCompany', groupId],
    count: (groupId: string) => [
      ...groupsKeys.multiCompany.list(groupId),
      'count',
    ],
  },
  poll: {
    all: () => [...groupsKeys.all(), 'poll'],
    livestream: (livestreamId: string) => [
      ...groupsKeys.poll.all(),
      'livestream',
      livestreamId,
    ],
  },
};

export const invalidateGroupNotifications = () =>
  queryClient.invalidateQueries(
    groupsKeys.notifications.notificationRedBubblesCount(),
  );

export const invalidateExploreGroups = () =>
  queryClient.invalidateQueries(groupsKeys.allExploreGroups());

export const invalidateGroupRequests = (groupId: string) =>
  queryClient.invalidateQueries(groupsKeys.allRequests(groupId));

export const invalidateGroupList = () => {
  queryClient.invalidateQueries(groupsKeys.allList());
  queryClient.invalidateQueries(groupsKeys.allMyGroups());
};

export const invalidateAllGroupLists = () => {
  invalidateGroupList();
  invalidateExploreGroups();
};

export const invalidateGroupScheduledPostList = (groupId: number | string) =>
  queryClient.invalidateQueries(groupsKeys.scheduledPostList(`${groupId}`));

export const invalidateGroupDetails = (groupId: number | string) =>
  queryClient.invalidateQueries(groupsKeys.detail(`${groupId}`));

export const invalidateGroupPinnedPost = (groupId: number | string) =>
  queryClient.invalidateQueries(groupsKeys.postPinList(`${groupId}`));

export const invalidateGroupPostList = (groupId: number | string) =>
  queryClient.invalidateQueries(groupsKeys.postList(`${groupId}`));

export const invalidatePinAndPostList = (groupId: number) => {
  invalidateGroupPinnedPost(`${groupId}`);
  invalidateGroupPostList(groupId);
};

export const invalidateAllTranslations = () =>
  queryClient.invalidateQueries(groupsKeys.translate.all());

export const invalidateGroupPendingApprovalPostList = (
  groupId: number | string | undefined,
) => {
  if (!groupId) return;

  queryClient.invalidateQueries(
    groupsKeys.pendingApproval.postList(`${groupId}`),
  );
};

export const invalidateGroup = (groupId: string | number) => {
  invalidateGroupList();
  invalidateGroupDetails(groupId);
};

export const groupPostDeleted = (groupId: string | number) => {
  invalidateGroupPostList(groupId);
  invalidateGroupPinnedPost(groupId);
  queryClient.invalidateQueries(groupsKeys.searchAllPost(`${groupId}`));
};

export const invalidateGroupPost = (
  groupId: string | number,
  postId: string | number,
) => {
  groupPostDeleted(groupId);
  queryClient.invalidateQueries(
    groupsKeys.postDetail(`${groupId}`, `${postId}`),
  );
};

export const groupPostUpdated = (
  groupId: string | number,
  postId: string | number,
) => {
  groupPostDeleted(groupId);
  queryClient.invalidateQueries(
    sharedPostKeys.groups.detail(Number(groupId), Number(postId)),
  );
};

export const updateGroupMembersCount = (
  groupId: number,
  membersCount: number,
) =>
  queryClient.setQueryData<AxiosResponse<Group> | undefined>(
    groupsKeys.detail(`${groupId}`),
    oldData => {
      if (!oldData) return oldData;

      oldData.data.membersCount = membersCount;

      return oldData;
    },
  );

export const setGroupFeedDetailData = (
  postItemId: number,
  groupId: string,
  updater: Updater<GroupPost>,
) => {
  queryClient.setQueryData<AxiosResponse<GroupPost> | undefined>(
    groupsKeys.postDetail(groupId, `${postItemId}`),
    oldData => {
      if (!oldData) return oldData;

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

export const addGroupFeedDetailDataReaction = (
  postItemId: number,
  groupId: string,
  emoji: string,
  unified?: string,
  isLoggedUserReaction: boolean = true,
) => {
  setGroupFeedDetailData(postItemId, groupId, oldPost => {
    if (!oldPost) return;

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

    sortReactions(oldPost.reactions, DEFAULT_EMOJI.emoji);

    return oldPost;
  });
};

export const removeGroupFeedDetailDataReaction = (
  postItemId: number,
  groupId: string,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setGroupFeedDetailData(postItemId, groupId, oldPost => {
    if (!oldPost) return;

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

    sortReactions(oldPost.reactions, DEFAULT_EMOJI.emoji);

    return oldPost;
  });
};

export const setGroupLastCommentDataPostDetail = (
  postId: number,
  groupId: string,
  commentId: number,
  updater: Updater<Comment>,
) => {
  setGroupFeedDetailData(postId, groupId, oldPost =>
    lastCommentUpdater(oldPost, commentId, updater),
  );
};

export const setGroupChildLastCommentDataPostDetail = (
  postId: number,
  groupId: string,
  commentId: number,
  childId: number,
  updater: Updater<Comment>,
) => {
  setGroupLastCommentDataPostDetail(postId, groupId, commentId, oldComment =>
    childCommentUpdater(oldComment, childId, updater),
  );
};

export const pushGroupFeedListData = (postItem: GroupPost, id: string) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<GroupPostList>> | undefined
  >(groupsKeys.postList(id), oldData => {
    if (!oldData?.pages?.[0]?.data?.items) return oldData;

    const newPost = { ...postItem, commentCount: postItem.commentCount || 0 };
    oldData.pages[0].data.items.splice(0, 0, newPost);
    return { ...oldData, _updated: Date.now() };
  });
};

const updatePostListData =
  (postItemId: number, updater: Updater<GroupPost>) =>
  (oldData: InfiniteData<AxiosResponse<GroupPostList>> | undefined) => {
    if (!oldData) return oldData;

    const [pageIndex, postIndex] = findPostIndexOnGroupPages(
      postItemId,
      oldData.pages,
    );

    if (pageIndex === -1 || postIndex === -1) return oldData;

    oldData.pages[pageIndex].data.items[postIndex] = functionalUpdate(
      updater,
      oldData.pages[pageIndex].data.items[postIndex],
    );
    return { ...oldData, _updated: Date.now() };
  };

export const setGroupFeedListData = (
  postItemId: number,
  groupId: string,
  updater: Updater<GroupPost>,
) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<GroupPostList>> | undefined
  >(groupsKeys.postList(groupId), updatePostListData(postItemId, updater));
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<GroupPostList>> | undefined
  >(groupsKeys.postPinList(groupId), updatePostListData(postItemId, updater));
};

export const updateGroupVoteInPoll = (
  postId: number,
  groupId: string,
  pollOptionId: number,
  isLoggedUserAnswer: boolean = true,
) => {
  const updater = (oldPost: GroupPost | undefined) => {
    if (!oldPost) return oldPost;
    return addVote(oldPost, pollOptionId, isLoggedUserAnswer) ?? undefined;
  };
  setGroupFeedListData(postId, groupId, updater);
  setGroupFeedDetailData(postId, groupId, updater);
  setAllGroupSearchPostListData(postId, groupId, updater);
};

export const addGroupFeedListDataReaction = (
  postItemId: number,
  groupId: string,
  emoji: string,
  unified: string,
  isLoggedUserReaction: boolean = true,
) => {
  setGroupFeedListData(postItemId, groupId, oldPost => {
    if (!oldPost) return;

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

    sortReactions(oldPost.reactions, DEFAULT_EMOJI.emoji);

    return oldPost;
  });
};

export const removeGroupFeedListDataReaction = (
  postItemId: number,
  groupId: string,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setGroupFeedListData(postItemId, groupId, oldPost => {
    if (!oldPost) return;

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

    sortReactions(oldPost.reactions, DEFAULT_EMOJI.emoji);

    return oldPost;
  });
};

const dropCommentUpdater = (
  oldPost: DataUpdate<GroupPost>,
  commentId: number,
) => {
  if (!oldPost?.lastComments) return oldPost;

  const commentIndex = oldPost.lastComments.findIndex(
    comment => comment.id === commentId,
  );

  if (commentIndex !== -1) {
    oldPost.commentCount = decreaseCommentCount(oldPost.commentCount);
    oldPost.lastComments.splice(commentIndex, 1);
  }

  return oldPost;
};

export const dropGroupComment = (
  postId: number,
  groupId: string,
  commentId: number,
) => {
  const updater = (oldPost: DataUpdate<GroupPost>) =>
    dropCommentUpdater(oldPost, commentId);
  setGroupFeedListData(postId, groupId, updater);
  setGroupFeedDetailData(postId, groupId, updater);
  setAllGroupSearchPostListData(postId, groupId, updater);
  queryClient.invalidateQueries(feedKeys.keyUpdates.all());
  dropGroupCommentDataCommentList(postId, groupId, commentId);
  dropGroupCommentDataChildCommentList(commentId, groupId);
  queryClient.invalidateQueries(groupsKeys.comment.detail(postId, groupId));
  queryClient.invalidateQueries(groupsKeys.postList(groupId));
  queryClient.invalidateQueries(groupsKeys.postPinList(groupId));
};

const dropChildCommentUpdater = (
  oldComment: DataUpdate<Comment>,
  childId: number,
) => {
  if (!oldComment?.children) return oldComment;

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

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

  return oldComment;
};

export const dropGroupChildComment = (
  postId: number,
  groupId: string,
  parentId: number,
  childId: number,
) => {
  const updater = (oldComment: DataUpdate<Comment>) =>
    dropChildCommentUpdater(oldComment, childId);
  setGroupLastCommentDataFeedList(postId, groupId, parentId, updater);
  setGroupLastCommentDataPostDetail(postId, groupId, parentId, updater);
  setGroupCommentListData(postId, groupId, parentId, updater);
  setGroupLastCommentDataAllSearchList(postId, groupId, parentId, updater);
  dropGroupChildCommentDataChildCommentList(parentId, groupId, childId);
  queryClient.invalidateQueries(groupsKeys.comment.detail(postId, groupId));
  queryClient.invalidateQueries(
    groupsKeys.childComment.detail(parentId, groupId),
  );
};

export const updateGroupComment = (
  postId: number,
  groupId: string,
  comment: Comment,
) => {
  setGroupCommentListData(postId, groupId, comment.id, comment);
  setGroupLastCommentDataFeedList(postId, groupId, comment.id, comment);
  setLastCommentDataFeedList(postId, comment.id, comment);
  setGroupLastCommentDataPostDetail(postId, groupId, comment.id, comment);
  setGroupLastCommentDataAllSearchList(postId, groupId, comment.id, comment);
  queryClient.invalidateQueries(groupsKeys.comment.detail(postId, groupId));
  queryClient.invalidateQueries(feedKeys.keyUpdates.all());
};

export const updateGroupChildComment = (
  postId: number,
  groupId: string,
  parentId: number,
  comment: Comment,
) => {
  setGroupChildCommentDataCommentList(
    postId,
    groupId,
    parentId,
    comment.id,
    comment,
  );
  setGroupChildCommentDataChildCommentList(
    parentId,
    groupId,
    comment.id,
    comment,
  );
  setGroupChildLastCommentDataFeedList(
    postId,
    groupId,
    parentId,
    comment.id,
    comment,
  );
  setGroupChildLastCommentDataPostDetail(
    postId,
    groupId,
    parentId,
    comment.id,
    comment,
  );
  setGroupChildLastCommentDataAllSearchList(
    postId,
    groupId,
    parentId,
    comment.id,
    comment,
  );
  // TODO: check if needed
  // queryClient.invalidateQueries(groupsKeys.comment.detail(postId, groupId));
  queryClient.invalidateQueries(
    groupsKeys.childComment.detail(parentId, groupId),
  );
};

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

    const [pageIndex, childIndex] = findIndexOnGroupPagesAny(
      oldData.pages,
      child => child.id === childId,
    );

    if (pageIndex !== -1 && childIndex !== -1) {
      removeCommentFromPages(oldData.pages, pageIndex, childIndex);
    }

    return oldData;
  });
};

export const dropGroupCommentDataCommentList = (
  postId: number,
  groupId: string,
  commentId: number,
) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<GroupCommentList | Comment[]>> | undefined
  >(groupsKeys.comment.detail(postId, groupId), oldData => {
    if (!oldData) return oldData;

    const [pageIndex, commentIndex] = findIndexOnGroupPagesAny(
      oldData.pages,
      comment => comment.id === commentId,
    );

    if (pageIndex !== -1 && commentIndex !== -1) {
      removeCommentFromPages(oldData.pages, pageIndex, commentIndex);
    }

    return oldData;
  });
};

export const dropGroupCommentDataChildCommentList = (
  commentId: number,
  groupId: string,
) => {
  queryClient.removeQueries(groupsKeys.childComment.detail(commentId, groupId));
};

export const reactionCommentUpdater = (
  oldComment: DataUpdate<Comment>,
  updater: Updater<Reaction[]>,
) => {
  if (!oldComment) return oldComment;

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

  sortReactions(oldComment.reactions, DEFAULT_EMOJI.emoji);

  return oldComment;
};

export const updateGroupReactionCommentData = (
  postId: number,
  groupId: string,
  commentId: number,
  updater: Updater<Reaction[]>,
) => {
  const updaterAux = (oldComment: DataUpdate<Comment>) =>
    reactionCommentUpdater(oldComment, updater);
  setGroupLastCommentDataFeedList(postId, groupId, commentId, updaterAux);
  setGroupLastCommentDataPostDetail(postId, groupId, commentId, updaterAux);
  setGroupLastCommentDataAllSearchList(postId, groupId, commentId, updaterAux);
  setGroupCommentListData(postId, groupId, commentId, updaterAux);
};

export const updateGroupReactionCommentDataForFeed = (
  postId: number,
  groupId: string,
  commentId: number,
  updater: Updater<Reaction[]>,
) => {
  const updaterAux = (oldComment: DataUpdate<Comment>) =>
    reactionCommentUpdater(oldComment, updater);
  setLastCommentDataFeedList(postId, commentId, updaterAux);
  setLastCommentDataKeyUpdatesList(postId, commentId, updaterAux);
  setLastCommentDataPostDetail(postId, commentId, updaterAux);
  setLastCommentDataAllSearchList(postId, commentId, updaterAux);
  setGroupCommentListData(postId, groupId, commentId, updaterAux);
};

const childCommentUpdater = (
  oldComment: DataUpdate<Comment>,
  childId: number,
  updater: Updater<Comment>,
) => {
  if (!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;
};

const setGroupChildCommentDataCommentList = (
  postId: number,
  groupId: string,
  commentId: number,
  childId: number,
  updater: Updater<Comment>,
) => {
  setGroupCommentListData(postId, groupId, commentId, oldComment =>
    childCommentUpdater(oldComment, childId, updater),
  );
};

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

    const [pageIndex, childIndex] = findIndexOnGroupPagesAny(
      oldData.pages,
      child => child.id === childId,
    );

    if (pageIndex !== -1 && childIndex !== -1) {
      updateCommentInPages(oldData.pages, pageIndex, childIndex, updater);
    }

    return oldData;
  });
};

const setGroupChildLastCommentDataFeedList = (
  postId: number,
  groupId: string,
  commentId: number,
  childId: number,
  updater: Updater<Comment>,
) => {
  setGroupLastCommentDataFeedList(postId, groupId, commentId, oldComment =>
    childCommentUpdater(oldComment, childId, updater),
  );
};

const setGroupChildLastCommentDataAllSearchList = (
  postId: number,
  groupId: string,
  commentId: number,
  childId: number,
  updater: Updater<Comment>,
) => {
  setGroupLastCommentDataAllSearchList(postId, groupId, commentId, oldComment =>
    childCommentUpdater(oldComment, childId, updater),
  );
};

const reactionUpdater = (
  reactions: Reaction[],
  updater: Updater<Reaction[]>,
) => {
  if (!reactions) return reactions;

  reactions = functionalUpdate(updater, reactions);

  sortReactions(reactions, DEFAULT_EMOJI.emoji);

  return reactions;
};

export const updateGroupSearchPostListDataReaction = (
  postItemId: number,
  groupId: string,
  query: string,
  updater: Updater<Reaction[]>,
) => {
  setGroupSearchPostListData(postItemId, groupId, query, oldPost => {
    if (!oldPost) return;

    oldPost.reactions = reactionUpdater(oldPost.reactions, updater);

    return oldPost;
  });
};

export const updateGroupReactionChildCommentData = (
  postId: number,
  groupId: string,
  commentId: number,
  childId: number,
  updater: Updater<Reaction[]>,
) => {
  const updaterAux = (oldComment: DataUpdate<Comment>) =>
    reactionCommentUpdater(oldComment, updater);
  setGroupChildCommentDataCommentList(
    postId,
    groupId,
    commentId,
    childId,
    updaterAux,
  );
  setGroupChildCommentDataChildCommentList(
    commentId,
    groupId,
    childId,
    updaterAux,
  );
  setGroupChildLastCommentDataFeedList(
    postId,
    groupId,
    commentId,
    childId,
    updaterAux,
  );
  setGroupChildLastCommentDataPostDetail(
    postId,
    groupId,
    commentId,
    childId,
    updaterAux,
  );
  setGroupChildLastCommentDataAllSearchList(
    postId,
    groupId,
    commentId,
    childId,
    updaterAux,
  );
};

export const setGroupSearchPostListData = (
  postItemId: number,
  groupId: string,
  query: string,
  updater: Updater<GroupPost>,
) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<GroupPostList>> | undefined
  >(groupsKeys.searchPost(groupId, query), oldData => {
    if (!oldData) return oldData;

    const [pageIndex, postIndex] = findPostIndexOnGroupPages(
      postItemId,
      oldData.pages,
    );

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

    return oldData;
  });
};

const setAllGroupSearchPostListData = (
  postItemId: number,
  groupId: string,
  updater: Updater<GroupPost>,
) => {
  queryClient.setQueriesData<
    InfiniteData<AxiosResponse<GroupPostList>> | undefined
  >(groupsKeys.searchAllPost(groupId), oldData => {
    if (!oldData) return oldData;

    const [pageIndex, postIndex] = findPostIndexOnGroupPages(
      postItemId,
      oldData.pages,
    );

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

    return oldData;
  });
};

export const unshiftGroupComment = (
  postId: number,
  groupId: string,
  comment: Comment,
) => {
  const updater = (oldPost: DataUpdate<GroupPost | Post>) =>
    unshiftCommentUpdater(oldPost, comment);
  setAllGroupSearchPostListData(
    postId,
    groupId,
    updater as (oldPost: DataUpdate<GroupPost>) => GroupPost,
  );
  setGroupFeedDetailData(
    postId,
    groupId,
    updater as (oldPost: DataUpdate<GroupPost>) => GroupPost,
  );
  setGroupFeedListData(
    postId,
    groupId,
    updater as (oldPost: DataUpdate<GroupPost>) => GroupPost,
  );
  setFeedListData(postId, oldPost => unshiftCommentUpdater(oldPost, comment));
  queryClient.invalidateQueries(feedKeys.keyUpdates.all());
  queryClient.invalidateQueries(groupsKeys.comment.liveStream(postId, groupId));
};

const setGroupLastCommentDataFeedList = (
  postId: number,
  groupId: string,
  commentId: number,
  updater: Updater<Comment>,
) => {
  setGroupFeedListData(postId, groupId, oldPost =>
    lastCommentUpdater(oldPost, commentId, updater),
  );
};

// Helper function that handles both transformed (flat array) and non-transformed (with .items) data
// Used for cache updates that may encounter either data structure
const findIndexOnGroupPagesAny = (
  pages: AxiosResponse<GroupCommentList | Comment[]>[],
  compare: (element: Comment) => boolean,
): [pageIndex: number, elementIndex: number] => {
  if (!pages) return [-1, -1];

  for (let i = 0; i < pages.length; i++) {
    const pageData = pages[i]?.data;

    // Check if data is in transformed format (flat array) or original format (with .items)
    const items = Array.isArray(pageData)
      ? pageData
      : (pageData as GroupCommentList)?.items;

    if (!items) continue;

    const elementIndex = items.findIndex(compare);
    if (elementIndex !== undefined && elementIndex !== -1) {
      return [i, elementIndex];
    }
  }

  return [-1, -1];
};

// Helper functions to handle dual-structure data (Comment[] vs GroupCommentList.items)
const updateCommentInPages = (
  pages: AxiosResponse<GroupCommentList | Comment[]>[],
  pageIndex: number,
  itemIndex: number,
  updater: Updater<Comment>,
) => {
  const pageData = pages[pageIndex].data;
  if (Array.isArray(pageData)) {
    pageData[itemIndex] = functionalUpdate(updater, pageData[itemIndex]);
  } else {
    pageData.items[itemIndex] = functionalUpdate(
      updater,
      pageData.items[itemIndex],
    );
  }
};

const removeCommentFromPages = (
  pages: AxiosResponse<GroupCommentList | Comment[]>[],
  pageIndex: number,
  itemIndex: number,
) => {
  const pageData = pages[pageIndex].data;
  if (Array.isArray(pageData)) {
    pageData.splice(itemIndex, 1);
  } else {
    pageData.items.splice(itemIndex, 1);
  }
};

const unshiftCommentToPages = (
  pages: AxiosResponse<GroupCommentList | Comment[]>[],
  pageIndex: number,
  comment: Comment,
  includesCheck: (comments: Comment[], comment: Comment) => boolean,
) => {
  const pageData = pages[pageIndex].data;
  if (Array.isArray(pageData)) {
    if (!includesCheck(pageData, comment)) {
      pageData.unshift(comment);
    }
  } else {
    if (!includesCheck(pageData.items, comment)) {
      pageData.items.unshift(comment);
    }
  }
};

export const unshiftGroupChildComment = (
  postId: number,
  groupId: string,
  parentId: number,
  childComment: Comment,
) => {
  const updater = (oldComment: DataUpdate<Comment>) =>
    unshiftChildUpdater(oldComment, childComment);
  setGroupCommentListData(postId, groupId, parentId, updater);
  setGroupLastCommentDataAllSearchList(postId, groupId, parentId, updater);
  setGroupLastCommentDataPostDetail(postId, groupId, parentId, updater);
  setGroupLastCommentDataFeedList(postId, groupId, parentId, updater);
  setGroupLastCommentDataKeyUpdatesList(postId, parentId, updater);
  unshiftGroupChildCommentData(parentId, groupId, childComment);
};

const setGroupLastCommentDataAllSearchList = (
  postId: number,
  groupId: string,
  commentId: number,
  updater: Updater<Comment>,
) => {
  setAllGroupSearchPostListData(postId, groupId, oldPost =>
    lastCommentUpdater(oldPost, commentId, updater),
  );
};

const setGroupLastCommentDataKeyUpdatesList = (
  postId: number,
  commentId: number,
  updater: Updater<Comment>,
) => {
  setKeyUpdatesListData(postId, oldPost =>
    lastCommentUpdater(oldPost, commentId, updater),
  );
};

const setGroupCommentListData = (
  postItemId: number,
  groupId: string,
  commentId: number,
  updater: Updater<Comment>,
) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<GroupCommentList | Comment[]>> | undefined
  >(groupsKeys.comment.detail(postItemId, groupId), oldData => {
    if (!oldData) return oldData;

    const [pageIndex, childIndex] = findIndexOnGroupPagesAny(
      oldData.pages,
      comment => comment.id === commentId,
    );

    if (pageIndex !== -1 && childIndex !== -1) {
      updateCommentInPages(oldData.pages, pageIndex, childIndex, updater);
    }

    return oldData;
  });
};

const unshiftGroupChildCommentData = (
  commentId: number,
  groupId: string,
  childComment: Comment,
) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<GroupCommentList | Comment[]>> | undefined
  >(groupsKeys.childComment.detail(commentId, groupId), oldData => {
    if (!oldData?.pages) return oldData;

    unshiftCommentToPages(oldData.pages, 0, childComment, includesComment);

    return oldData;
  });
};

export const removePinnedGroup = (groupId: number) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<Pagination<Group[]>>> | undefined
  >(groupsKeys.allPinnedList(), oldData => {
    if (!oldData) return oldData;

    oldData.pages = oldData.pages.map(page => {
      page.data.items = page.data.items.filter(group => group.id !== groupId);
      return page;
    });

    return oldData;
  });
};

export const removeUnpinnedGroup = (groupId: number) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<Pagination<Group[]>>> | undefined
  >(groupsKeys.allUnpinnedList(), oldData => {
    if (!oldData) return oldData;

    oldData.pages = oldData.pages.map(page => {
      page.data.items = page.data.items.filter(group => group.id !== groupId);
      return page;
    });

    return oldData;
  });
};

export const invalidateLivestreamGroupPollData = (livesteamId: string) => {
  queryClient.invalidateQueries(groupsKeys.poll.livestream(livesteamId));
};
