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

import { type AxiosResponse } from 'axios';

import { queryClient } from 'src/config/react-query';
import {
  type Acknowledgement,
  type Category,
} from 'src/pages/dashboard/acknowledgements/types';
import { type Post } from 'src/types/posts';
import { type Updater } from 'src/types/queries';
import { type CursorPagination } from 'src/types/services';
import {
  findIndexOnCursorPaginatedPages,
  findIndexOnPaginatedPages,
} from 'src/utils/profile';
import { functionalUpdate } from 'src/utils/queries';
import {
  addReaction,
  removeReaction,
  sortReactions,
} from 'src/utils/reactions';

export const profileKeys = {
  all: () => ['profile'],
  allDetail: () => [...profileKeys.all(), 'detail'],
  detail: (id: number | string) => [...profileKeys.allDetail(), Number(id)],
  bosses: (id: number | string) => [...profileKeys.detail(id), 'bosses'],
  userList: (name: string, status?: string) => [
    ...profileKeys.all(),
    name,
    ...(status ? [status] : []),
  ],
  userListSubordinates: (name: string) => [
    ...profileKeys.all(),
    name,
    'subordinates',
  ],
  userProfileData: (id: number | string, instanceId: number | string) => [
    ...profileKeys.detail(id),
    Number(instanceId),
    'userProfileData',
  ],
  profileFields: (id: number) => [...profileKeys.all(), id],
  acknowledgements: {
    all: (id: number | string) => [
      ...profileKeys.detail(id),
      'acknowledgements',
    ],
    list: (id: number | string) => [
      ...profileKeys.acknowledgements.all(id),
      'list',
    ],
  },
  feed: {
    all: () => [...profileKeys.all(), 'posts'],
    detail: (id: number | string) => [...profileKeys.feed.all(), Number(id)],
    list: (id: number | string) => [...profileKeys.feed.detail(id), 'list'],
  },
  groupFeed: {
    all: () => [...profileKeys.all(), 'group-posts'],
    detail: (id: number | string) => [
      ...profileKeys.groupFeed.all(),
      Number(id),
    ],
    list: (id: number | string) => [
      ...profileKeys.groupFeed.detail(id),
      'list',
    ],
  },
  timeTracking: {
    kioskPin: () => [...profileKeys.allDetail(), 'kiosk-pin'], // No params needed
  },
};

export const updateUserAcknowledgements = (
  acknowledgement: Acknowledgement,
) => {
  const recipients = acknowledgement.acknowledgeds?.length
    ? acknowledgement.acknowledgeds
    : [acknowledgement.acknowledged];

  recipients.forEach(recipient => {
    setUserAcknowledgementListData(
      recipient.id,
      acknowledgement.id,
      acknowledgement,
    );
  });
};

export const addUserAcknowledgement = (
  userId: number | string,
  acknowledgement: Acknowledgement,
  pageNumber: number = 0,
) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<Acknowledgement[]>> | undefined
  >(profileKeys.acknowledgements.list(userId), oldData => {
    if (!oldData?.pages?.length) return oldData;
    if (!oldData.pages[pageNumber].data) return oldData;

    oldData.pages[pageNumber].data = [
      acknowledgement,
      ...oldData.pages[pageNumber].data,
    ];

    return oldData;
  });
};

export const removeUserAcknowledgement = (acknowledgement: Acknowledgement) => {
  const recipients = acknowledgement.acknowledgeds?.length
    ? acknowledgement.acknowledgeds
    : [acknowledgement.acknowledged];

  recipients.forEach(recipient => {
    queryClient.setQueryData<
      InfiniteData<AxiosResponse<Acknowledgement[]>> | undefined
    >(profileKeys.acknowledgements.list(recipient.id), oldData => {
      if (!oldData?.pages?.length) return oldData;

      const [pageIndex, acknowledgementIndex] = findIndexOnPaginatedPages(
        acknowledgement.id,
        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 updateCategoryData = (userId: number, newCategory: Category) => {
  queryClient.setQueryData(
    profileKeys.acknowledgements.list(userId),
    (oldData: any) => {
      if (!oldData?.pages) 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;
    },
  );
};

export const setUserAcknowledgementListData = (
  userId: number,
  acknowledgementId: number,
  updater: Updater<Acknowledgement>,
) => {
  queryClient.setQueryData<
    InfiniteData<AxiosResponse<Acknowledgement[]>> | undefined
  >(profileKeys.acknowledgements.list(userId), 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 };
  });
};

const setUserFeedListData = (
  userId: number,
  postItemId: number,
  updater: Updater<Post>,
) => {
  queryClient.setQueryData(
    profileKeys.feed.list(userId),
    (oldData: any): any => {
      if (!oldData) return oldData;

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

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

      return oldData;
    },
  );
};

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

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

      sortReactions(oldAcknowledgement.reactions);

      return oldAcknowledgement;
    },
  );
};

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

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

      sortReactions(oldAcknowledgement.reactions);

      return oldAcknowledgement;
    },
  );
};

export const addUserFeedListDataReaction = (
  userId: number,
  postItemId: number,
  emoji: string,
  unified: string,
  isLoggedUserReaction: boolean = true,
) => {
  setUserFeedListData(userId, postItemId, oldPost => {
    if (!oldPost) return;

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

    sortReactions(oldPost.reactions);

    return oldPost;
  });
};

export const removeUserFeedListDataReaction = (
  userId: number,
  postItemId: number,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setUserFeedListData(userId, postItemId, oldPost => {
    if (!oldPost) return;

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

    sortReactions(oldPost.reactions);

    return oldPost;
  });
};

const setUserGroupFeedListData = (
  userId: number,
  postItemId: number,
  updater: Updater<Post>,
) => {
  queryClient.setQueryData(
    profileKeys.groupFeed.list(userId),
    (
      oldData:
        | InfiniteData<AxiosResponse<CursorPagination<Post[]>>>
        | undefined,
    ): any => {
      if (!oldData) return oldData;

      const [pageIndex, postIndex] = findIndexOnCursorPaginatedPages(
        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 addUserGroupFeedListDataReaction = (
  userId: number,
  postItemId: number,
  emoji: string,
  unified: string,
  isLoggedUserReaction: boolean = true,
) => {
  setUserGroupFeedListData(userId, postItemId, oldPost => {
    if (!oldPost) return;

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

    sortReactions(oldPost.reactions);

    return oldPost;
  });
};

export const removeUserGroupFeedListDataReaction = (
  userId: number,
  postItemId: number,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setUserGroupFeedListData(userId, postItemId, oldPost => {
    if (!oldPost) return;

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

    sortReactions(oldPost.reactions);

    return oldPost;
  });
};
