import { InfiniteData } from 'react-query';

import { AxiosResponse } from 'axios';

import { queryClient } from 'src/config/react-query';
import { FOLDER_PENDING_ROOT } from 'src/constants/documents';
import {
  Folder,
  FolderResponse,
  FolderListResponse,
  Document,
  DocumentListResponse,
  SignatureStatus,
} from 'src/types/documents';
import { Updater } from 'src/types/queries';
import { getNextSignDocument } from 'src/utils/documents';
import { findIndexOnPaginatedPages } from 'src/utils/pages';
import { functionalUpdate } from 'src/utils/queries';

export const documentsKeys = {
  all: () => ['documents'],
  folder: {
    all: () => [...documentsKeys.all(), 'folder'],
    list: () => [...documentsKeys.folder.all(), 'list'],
    allDetail: () => [...documentsKeys.folder.all(), 'detail'],
    detail: (folderId: number | string) => [
      ...documentsKeys.folder.allDetail(),
      Number(folderId),
    ],
  },
  document: {
    all: () => [...documentsKeys.all(), 'document'],
    allList: () => [...documentsKeys.document.all(), 'list'],
    list: (folderId?: number | string, signatureStatus?: SignatureStatus) => [
      ...documentsKeys.document.allList(),
      Number(folderId),
      signatureStatus,
    ],
    pendingList: (folderId?: number | string) =>
      documentsKeys.document.list(folderId, SignatureStatus.PENDING),
    allDetail: () => [...documentsKeys.document.all(), 'detail'],
    detail: (documentId: number | string) => [
      ...documentsKeys.document.allDetail(),
      Number(documentId),
    ],
  },
  notifications: {
    all: () => [...documentsKeys.all(), 'notifications'],
    module: () => [...documentsKeys.notifications.all(), 'module'],
  },
};

// Folder

export const setFolderListData = (
  folderId: number,
  updater: Updater<Folder>,
) => {
  queryClient.setQueryData(
    documentsKeys.folder.list(),
    (
      oldData: InfiniteData<AxiosResponse<FolderListResponse>>,
    ): InfiniteData<AxiosResponse<FolderListResponse>> => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, folderIndex] = findIndexOnPaginatedPages(
        oldData?.pages,
        folder => folder.id === folderId,
      );

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

      return oldData;
    },
  );
};

export const pushFolderListData = () =>
  queryClient.invalidateQueries(documentsKeys.folder.list());

export const dropFolderListData = (folderId: number) => {
  queryClient.setQueryData(
    documentsKeys.folder.list(),
    (
      oldData: InfiniteData<AxiosResponse<FolderListResponse>>,
    ): InfiniteData<AxiosResponse<FolderListResponse>> => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, folderIndex] = findIndexOnPaginatedPages(
        oldData?.pages,
        folder => folder.id === folderId,
      );

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

      return oldData;
    },
  );
};

export const setFolderData = (folderId: number, updater: Updater<Folder>) => {
  queryClient.setQueryData(
    documentsKeys.folder.detail(folderId),
    (oldData: AxiosResponse<FolderResponse>): AxiosResponse<FolderResponse> => {
      if (!oldData?.data) return oldData;

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

      return oldData;
    },
  );
};

// Document

export const setDocumentListData = (
  documentId: number,
  updater: Updater<Document>,
  folderId?: number,
) => {
  queryClient.setQueryData(
    documentsKeys.document.list(folderId),
    (
      oldData: InfiniteData<AxiosResponse<DocumentListResponse>>,
    ): InfiniteData<AxiosResponse<DocumentListResponse>> => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, documentIndex] = findIndexOnPaginatedPages(
        oldData?.pages,
        document => document.id === documentId,
      );

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

      return oldData;
    },
  );
};

export const pushDocumentListData = (folderId?: number) =>
  queryClient.invalidateQueries(documentsKeys.document.list(folderId));

export const dropDocumentListData = (documentId: number, folderId?: number) => {
  queryClient.setQueryData(
    documentsKeys.document.list(folderId),
    (
      oldData: InfiniteData<AxiosResponse<DocumentListResponse>>,
    ): InfiniteData<AxiosResponse<DocumentListResponse>> => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, documentIndex] = findIndexOnPaginatedPages(
        oldData?.pages,
        document => document.id === documentId,
      );

      if (pageIndex !== -1 && documentIndex !== -1) {
        const elemenDeleted = oldData.pages[pageIndex].data.items.splice(
          documentIndex,
          1,
        );
        if (elemenDeleted[0].canBeSigned) {
          getNextSignDocument(oldData.pages);
        }
      }
      return oldData;
    },
  );
};

// Pending documents

export const setPendingDocumentListData = (
  documentId: number,
  updater: Updater<Document>,
  folderId?: number,
) => {
  queryClient.setQueryData(
    documentsKeys.document.pendingList(folderId || FOLDER_PENDING_ROOT),
    (
      oldData: InfiniteData<AxiosResponse<DocumentListResponse>>,
    ): InfiniteData<AxiosResponse<DocumentListResponse>> => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, documentIndex] = findIndexOnPaginatedPages(
        oldData?.pages,
        document => document.id === documentId,
      );

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

      return oldData;
    },
  );
};

export const pushPendingDocumentListData = (folderId?: number) =>
  queryClient.invalidateQueries(
    documentsKeys.document.pendingList(folderId || FOLDER_PENDING_ROOT),
  );

export const dropPendingDocumentListData = (
  documentId: number,
  folderId?: number,
) => {
  queryClient.setQueryData(
    documentsKeys.document.pendingList(folderId || FOLDER_PENDING_ROOT),
    (
      oldData: InfiniteData<AxiosResponse<DocumentListResponse>>,
    ): InfiniteData<AxiosResponse<DocumentListResponse>> => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, documentIndex] = findIndexOnPaginatedPages(
        oldData?.pages,
        document => document.id === documentId,
      );

      if (pageIndex !== -1 && documentIndex !== -1) {
        oldData.pages[pageIndex].data.items.splice(documentIndex, 1);
        oldData.pages[pageIndex].data.count =
          oldData.pages[pageIndex].data.count - 1;
        if (oldData.pages[pageIndex].data.items.length) {
          oldData.pages[pageIndex].data.items[0].canBeSigned = true;
        }
      }

      return oldData;
    },
  );
};

export const updateDocuments = () => {
  queryClient.invalidateQueries(documentsKeys.notifications.module());
  queryClient.invalidateQueries(documentsKeys.folder.list());
};

export const invalidatePendingDocumentList = () => {
  queryClient.invalidateQueries(documentsKeys.document.pendingList());
};
