import { type InfiniteData } from 'react-query';

import { type AxiosResponse } from 'axios';

import { queryClient } from 'src/config/react-query';
import { type GetProductsParams } from 'src/pages/dashboard/acknowledgements/Exchanges/types';
import {
  type Acknowledgement,
  type Category,
  type FormatedRankingFilters,
} from 'src/pages/dashboard/acknowledgements/types';
import { findIndexOnPaginatedPages } from 'src/pages/dashboard/acknowledgements/utils';
import { type Comment } from 'src/types/comments';
import { type Updater } from 'src/types/queries';
import { type Reaction } from 'src/types/reaction';
import { findIndexOnPages } from 'src/utils/pages';
import { functionalUpdate } from 'src/utils/queries';
import {
  addReaction,
  removeReaction,
  sortReactions,
} from 'src/utils/reactions';

import { reactionUpdater } from 'src/components/comments/queries';

export const acknowledgementsKeys = {
  all: () => ['acknowledgements'],
  allList: () => [...acknowledgementsKeys.all(), 'list'],
  list: (categoryId: number | null = null) => [
    ...acknowledgementsKeys.allList(),
    categoryId,
  ],
  allDetail: () => [...acknowledgementsKeys.all(), 'detail'],
  detail: (id?: number) => [...acknowledgementsKeys.allDetail(), id],
  comment: {
    all: () => [...acknowledgementsKeys.all(), 'comment'],
    list: () => [...acknowledgementsKeys.comment.all(), 'list'],
    allDetail: () => [...acknowledgementsKeys.comment.all(), 'detail'],
    detail: (id: number | string) => [
      ...acknowledgementsKeys.comment.allDetail(),
      Number(id),
    ],
  },
  childComment: {
    all: () => [...acknowledgementsKeys.all(), 'childComment'],
    list: () => [...acknowledgementsKeys.childComment.all(), 'list'],
    allDetail: () => [...acknowledgementsKeys.childComment.all(), 'detail'],
    detail: (id: number | string) => [
      ...acknowledgementsKeys.childComment.allDetail(),
      Number(id),
    ],
  },
  categories: {
    all: () => [...acknowledgementsKeys.all(), 'categories'],
    active: () => [...acknowledgementsKeys.all(), 'categories', 'active'],
  },
  ranking: {
    all: () => [...acknowledgementsKeys.all(), 'ranking'],
    filtered: (filters: FormatedRankingFilters) => [
      ...acknowledgementsKeys.all(),
      'ranking',
      filters,
    ],
  },
  segmentations: {
    all: () => [...acknowledgementsKeys.all(), 'segmentations'],
    groups: () => [...acknowledgementsKeys.segmentations.all(), 'groups'],
  },
  availablePoints: () => [...acknowledgementsKeys.all(), 'availablePoints'],
  products: (params?: GetProductsParams) => [
    ...acknowledgementsKeys.all(),
    'products',
    params,
  ],
  exchanges: (userId?: number) => [
    ...acknowledgementsKeys.all(),
    'exchanges',
    userId,
  ],
};

export const updateAcknowledgements = () => {
  queryClient.invalidateQueries(acknowledgementsKeys.all());
};

export const updateAcknowledgementById = (acknowledgement: Acknowledgement) => {
  queryClient.invalidateQueries(
    acknowledgementsKeys.detail(acknowledgement.id),
  );
  setAcknowledgementListData(acknowledgement.id, acknowledgement);
};

export const updateCategoryListData = (categoryUpdated: Category) => {
  queryClient.setQueryData<AxiosResponse<Category[]> | undefined>(
    acknowledgementsKeys.categories.all(),
    oldData => {
      if (!oldData) return oldData;

      const categoryIndex = oldData.data?.findIndex(
        item => item.id === categoryUpdated.id,
      );

      if (categoryIndex !== -1) {
        oldData.data[categoryIndex] = functionalUpdate(
          categoryUpdated,
          oldData.data[categoryIndex],
        );
      }

      return oldData;
    },
  );
};

export const addAcknowledgementListDataReaction = (
  acknowledgementId: number,
  emoji: string,
  unified: string,
  isLoggedUserReaction: boolean = true,
) => {
  setAcknowledgementListData(acknowledgementId, oldAcknowledgement => {
    if (!oldAcknowledgement) return oldAcknowledgement;

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

    sortReactions(oldAcknowledgement.reactions);

    return oldAcknowledgement;
  });
};

export const removeAcknowledgementListDataReaction = (
  acknowledgementId: number,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setAcknowledgementListData(acknowledgementId, oldAcknowledgement => {
    if (!oldAcknowledgement) return;

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

    sortReactions(oldAcknowledgement.reactions);

    return oldAcknowledgement;
  });
};

export const removeAcknowledgement = (acknowledgementId: number) => {
  queryClient.setQueriesData<
    InfiniteData<AxiosResponse<Acknowledgement[]>> | undefined
  >(acknowledgementsKeys.allList(), oldData => {
    if (!oldData?.pages?.length) return oldData;

    const [pageIndex, acknowledgementIndex] = findIndexOnPaginatedPages(
      acknowledgementId,
      oldData.pages,
    );

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

    const page = oldData.pages[pageIndex];
    const newPageData = [
      ...page.data.slice(0, acknowledgementIndex),
      ...page.data.slice(acknowledgementIndex + 1),
    ];

    const newPages = [...oldData.pages];
    newPages[pageIndex] = { ...page, data: newPageData };

    return { ...oldData, pages: newPages };
  });
};

export const setAcknowledgementListData = (
  acknowledgementId: number,
  updater: Updater<Acknowledgement>,
) => {
  queryClient.setQueriesData<
    InfiniteData<AxiosResponse<Acknowledgement[]>> | undefined
  >(acknowledgementsKeys.allList(), oldData => {
    if (!oldData?.pages?.length) return oldData;

    const [pageIndex, acknowledgementIndex] = findIndexOnPaginatedPages(
      acknowledgementId,
      oldData.pages,
    );

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

    const page = oldData.pages[pageIndex];
    const updatedItem = functionalUpdate(
      updater,
      page.data[acknowledgementIndex],
    );

    const newPageData = [...page.data];
    newPageData[acknowledgementIndex] = updatedItem as Acknowledgement;

    const newPages = [...oldData.pages];
    newPages[pageIndex] = { ...page, data: newPageData };

    return { ...oldData, pages: newPages };
  });
};

export const addAcknowledgementInListAcknowledgements = (
  acknowledgement: Acknowledgement,
  pageNumber: number = 0,
) => {
  queryClient.setQueriesData<
    InfiniteData<AxiosResponse<Acknowledgement[]>> | undefined
  >(acknowledgementsKeys.allList(), oldData => {
    if (!oldData?.pages?.length) return oldData;

    const targetPage = oldData.pages[pageNumber];
    if (!targetPage?.data) return oldData;

    const [pageIndex] = findIndexOnPaginatedPages(
      acknowledgement.id,
      oldData.pages,
    );

    if (pageIndex !== -1) return oldData;

    const newPages = [...oldData.pages];
    newPages[pageNumber] = {
      ...targetPage,
      data: [acknowledgement, ...targetPage.data],
    };

    return { ...oldData, pages: newPages };
  });
};

export const updateAcknowledgementListCategoryData = (
  newCategory: Category,
) => {
  queryClient.setQueriesData<
    InfiniteData<AxiosResponse<Acknowledgement[]>> | undefined
  >(acknowledgementsKeys.allList(), oldData => {
    if (!oldData?.pages?.length) return oldData;

    oldData.pages.forEach((page: AxiosResponse<Acknowledgement[]>) => {
      if (page?.data) {
        page.data = page.data.map(acknowledgement => {
          if (acknowledgement?.category?.id === newCategory.id) {
            acknowledgement.category = newCategory;
          }

          return acknowledgement;
        });
      }
    });

    return oldData;
  });
};

// COMMENTS
export const increaseAcknowledgementCommentAmount = (postItemId: number) => {
  setAcknowledgementListData(postItemId, oldPost => {
    if (!oldPost) return;

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

export const decreaseAcknowledgementCommentAmount = (postItemId: number) => {
  setAcknowledgementListData(postItemId, oldPost => {
    if (!oldPost) return;

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

export const dropLastCommentDataAcknowledgementList = (
  postId: number,
  commentId: number,
) => {
  setAcknowledgementListData(postId, oldPost => {
    if (!oldPost?.lastComments) return oldPost;

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

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

    return oldPost;
  });
};

export const unshiftCommentDataCommentList = (
  postId: number,
  comment: Comment,
) => {
  queryClient.setQueryData<InfiniteData<AxiosResponse<Comment[]>> | undefined>(
    acknowledgementsKeys.comment.detail(postId),
    oldData => {
      if (!oldData?.pages || oldData.pages.length === 0) return oldData;

      const newPages = [...oldData.pages];
      newPages[0] = {
        ...newPages[0],
        data: [comment, ...newPages[0].data],
      };
      return { ...oldData, pages: newPages };
    },
  );
};

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

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

      if (pageIndex !== -1 && commentIndex !== -1) {
        const newPages = [...oldData.pages];
        newPages[pageIndex] = {
          ...newPages[pageIndex],
          data: newPages[pageIndex].data.filter(
            comment => comment.id !== commentId,
          ),
        };
        return { ...oldData, pages: newPages };
      }

      return oldData;
    },
  );
};

export const dropCommentDataChildCommentList = (commentId: number) => {
  queryClient.removeQueries(
    acknowledgementsKeys.childComment.detail(commentId),
  );
};

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

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

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

      return oldData;
    },
  );
};

export const updateCommentListData = (
  postId: number,
  commentId: number,
  comment: Comment,
) => {
  setCommentListData(postId, 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 isCommentInCommentListData = (
  postId: number,
  commentId: number,
) => {
  let isComment = false;

  setCommentListData(postId, commentId, oldComment => {
    isComment = !!oldComment;
    return oldComment;
  });

  return isComment;
};

export const isCommentInLastCommentData = (
  postId: number,
  commentId: number,
) => {
  let isComment = false;

  setLastCommentDataAcknowledgementList(postId, commentId, oldComment => {
    isComment = !!oldComment;
    return oldComment;
  });

  return isComment;
};

export const isCommentInData = (postId: number, commentId: number) =>
  isCommentInCommentListData(postId, commentId) ||
  isCommentInLastCommentData(postId, commentId);

export const updateLastCommentData = (
  postId: number,
  commentId: number,
  comment: Comment,
) => {
  setLastCommentDataAcknowledgementList(postId, 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 setLastCommentDataAcknowledgementList = (
  postId: number,
  commentId: number,
  updater: Updater<Comment>,
) => {
  setAcknowledgementListData(postId, oldPost => {
    if (!oldPost?.lastComments) return oldPost;

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

    if (commentIndex !== -1) {
      oldPost.lastComments[commentIndex] = functionalUpdate(
        updater,
        oldPost.lastComments[commentIndex],
      );
    }

    return oldPost;
  });
};

export const pushLastCommentData = (postId: number, comment: Comment) => {
  setAcknowledgementListData(postId, oldPost => {
    if (!oldPost) return oldPost;

    if (!oldPost.lastComments) {
      oldPost.lastComments = [];
    }

    oldPost.lastComments.unshift(comment);

    return oldPost;
  });
};

export const updateReactionComment = (
  postId: number,
  commentId: number,
  updater: Updater<Reaction[]>,
) => {
  setCommentListData(postId, commentId, oldComment =>
    reactionUpdater(oldComment, updater),
  );
  setLastCommentDataAcknowledgementList(postId, commentId, oldComment =>
    reactionUpdater(oldComment, updater),
  );
};

// CHILD COMMENTS
export const setChildCommentDataCommentList = (
  postId: number,
  commentId: number,
  childId: number,
  updater: Updater<Comment>,
) => {
  setCommentListData(postId, commentId, oldComment => {
    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;
  });
};

export const setChildLastCommentDataAcknowledgementList = (
  postId: number,
  commentId: number,
  childId: number,
  updater: Updater<Comment>,
) => {
  setLastCommentDataAcknowledgementList(postId, commentId, oldComment => {
    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;
  });
};

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

      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 isChildCommentInCommentListData = (
  postId: number,
  commentId: number,
  childId: number,
) => {
  let isComment = false;

  setChildCommentDataCommentList(postId, commentId, childId, oldComment => {
    isComment = !!oldComment;
    return oldComment;
  });

  return isComment;
};

export const isChildCommentInLastCommentData = (
  postId: number,
  commentId: number,
  childId: number,
) => {
  let isComment = false;

  setChildLastCommentDataAcknowledgementList(
    postId,
    commentId,
    childId,
    oldComment => {
      isComment = !!oldComment;
      return oldComment;
    },
  );

  return isComment;
};

export const isChildCommentInChildCommentListData = (
  commentId: number,
  childId: number,
) => {
  let isComment = false;

  setChildCommentDataChildCommentList(commentId, childId, oldComment => {
    isComment = !!oldComment;
    return oldComment;
  });

  return isComment;
};

export const isChildCommentInData = (
  postId: number,
  commentId: number,
  childId: number,
) =>
  isChildCommentInCommentListData(postId, commentId, childId) ||
  isChildCommentInLastCommentData(postId, commentId, childId) ||
  isChildCommentInChildCommentListData(commentId, childId);

export const pushChildCommentDataCommentList = (
  postId: number,
  commentId: number,
  childComment: Comment,
) => {
  setCommentListData(postId, commentId, oldComment => {
    if (!oldComment) return oldComment;

    if (!oldComment.children) {
      oldComment.children = [];
    }

    oldComment.children.push(childComment);

    return oldComment;
  });
};

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

      oldData.pages[0].data.push(childComment);

      return oldData;
    },
  );
};

export const pushChildLastCommentDataAcknowledgementList = (
  postId: number,
  commentId: number,
  childComment: Comment,
) => {
  setLastCommentDataAcknowledgementList(postId, commentId, oldComment => {
    if (!oldComment) return oldComment;

    if (!oldComment.children) {
      oldComment.children = [];
    }

    oldComment.children.push(childComment);

    return oldComment;
  });
};

export const dropChildCommentDataCommentList = (
  postId: number,
  commentId: number,
  childId: number,
) => {
  setCommentListData(postId, commentId, oldComment => {
    if (!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>(
    acknowledgementsKeys.childComment.detail(commentId),
    oldData => {
      if (!oldData?.pages) return oldData;

      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;
    },
  );
};

export const dropChildLastCommentDataAcknowledgementList = (
  postId: number,
  commentId: number,
  childId: number,
) => {
  setLastCommentDataAcknowledgementList(postId, commentId, oldComment => {
    if (!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 updateReactionChildComment = (
  postId: number,
  commentId: number,
  childId: number,
  updater: Updater<Reaction[]>,
) => {
  setChildCommentDataCommentList(postId, commentId, childId, oldComment =>
    reactionUpdater(oldComment, updater),
  );
  setChildCommentDataChildCommentList(commentId, childId, oldComment =>
    reactionUpdater(oldComment, updater),
  );
  setChildLastCommentDataAcknowledgementList(
    postId,
    commentId,
    childId,
    oldComment => reactionUpdater(oldComment, updater),
  );
};
