import { InfiniteData } from 'react-query';

import { AxiosResponse } from 'axios';

import { queryClient } from 'src/config/react-query';
import { getUserSegmentations } from 'src/services/segmentations';
import {
  ChatListItem,
  ChatByIdResponse,
  ChatMessagesResponse,
  Message,
} from 'src/types/chats';
import {
  FormItemResponse,
  FormListResponse,
  FormPaginationResponse,
  FormChatsNotificationsResponse,
  FormType,
  FormListItem,
} from 'src/types/forms';
import { Updater } from 'src/types/queries';
import { updateMessage } from 'src/utils/chats';
import { findFormIndexOnPages, makePreSubmitStep } from 'src/utils/form';
import { findIndexOnPages, findIndexOnPaginatedPages } from 'src/utils/pages';
import { functionalUpdate } from 'src/utils/queries';
import {
  addReaction,
  removeReaction,
  sortReactions,
} from 'src/utils/reactions';

import { chatKeys } from 'src/components/dashboard/chat/queries';

export const formKeys = {
  all: () => ['forms'],
  listCompleted: (type: string, title: string = '') => [
    type,
    title,
    'completed',
  ],
  listAvailable: (type: string, title: string = '') => [
    type,
    title,
    'available',
  ],
  stats: () => [...formKeys.all(), 'stats'],
  description: (id: number | string) => [
    ...formKeys.all(),
    'description',
    Number(id),
  ],
  answer: (templateId: number | string, answerId: number | string) => [
    ...formKeys.all(),
    'template',
    Number(templateId),
    'answer',
    Number(answerId),
  ],
  request: () => [...formKeys.all(), 'requests'],
  redBubblesAgent: () => [...formKeys.all(), 'redBubblesAgent'],
  unassignedForms: (query?: string) => [
    ...formKeys.all(),
    'unassignedForms',
    query,
  ],
  assignedForms: (query?: string) => [
    ...formKeys.all(),
    'assignedForms',
    query,
  ],
  closedForms: (query?: string) => [...formKeys.all(), 'closedForms', query],
  allDetail: () => [...formKeys.request(), 'detail'],
  detail: (id: number | string) => [...formKeys.allDetail(), Number(id)],
  formApprovalWorkflow: (id: number) => ['formApprovalWorkflow', id],
  messages: {
    all: () => [...formKeys.all(), 'messages'],
    list: (id: number | string) => [...formKeys.messages.all(), Number(id)],
  },
  pdf: {
    all: () => [...formKeys.all(), 'pdf'],
    allBlob: () => [...formKeys.pdf.all(), 'blob'],
    blob: (formId: number | string, templateId: number | string) => [
      ...formKeys.pdf.allBlob(),
      Number(formId),
      Number(templateId),
    ],
  },
  notifications: {
    all: () => [...formKeys.all(), 'notifications'],
    module: () => [...formKeys.notifications.all(), 'module'],
  },
};

export const deleteUnassignedForm = () => {
  queryClient.invalidateQueries(formKeys.unassignedForms());
};

export const refetchFormsListCompleted = () => {
  queryClient.invalidateQueries(formKeys.listCompleted(FormType.FORM));
};

export const refetchAssignedForms = () => {
  queryClient.invalidateQueries(formKeys.assignedForms());
};

export const invalidateSpecificListsForms = (keyNameListsForms: string[]) => {
  queryClient.invalidateQueries(keyNameListsForms);
};

export const updateListsForms = async () => {
  await Promise.all([
    queryClient.invalidateQueries(formKeys.unassignedForms()),
    queryClient.invalidateQueries(formKeys.assignedForms()),
    queryClient.invalidateQueries(formKeys.closedForms()),
  ]);
};

export const moveFirsFormChatListData = (
  keyName: string[],
  formItemId: number,
  numberPage = 0,
) => {
  queryClient.setQueryData(
    keyName,
    (oldData: InfiniteData<AxiosResponse<FormListResponse>>) => {
      if (!oldData?.pages.length) return oldData;

      const chatItem = oldData.pages[numberPage].data.find(
        item => item.formId === formItemId,
      );

      if (chatItem) {
        oldData.pages[numberPage].data = [
          chatItem,
          ...oldData.pages[numberPage].data.filter(
            item => item.formId !== chatItem.formId,
          ),
        ];
      }

      return oldData;
    },
  );
};

export const formInFirstPage = (keyName: string[], formItemId: number) => {
  const formPageListData: InfiniteData<AxiosResponse<FormPaginationResponse>> =
    queryClient.getQueryData(keyName);

  const formIndex = formPageListData?.pages[0]?.data?.items.findIndex(
    item => item.formId === formItemId,
  );

  return formIndex !== -1;
};

// TODO: esta funcion es parecida a formInFirstPage si el back a futuro
// aplica un cambio en al respuesta de los forms de los agentes
// se debera eliminar esta funcion y usar formInFirstPage donde corresponda
export const formAgentInFirstPage = (keyName: string[], formItemId: number) => {
  const formPageListData: InfiniteData<AxiosResponse<FormListResponse>> =
    queryClient.getQueryData(keyName);

  const formIndex = formPageListData?.pages[0]?.data?.findIndex(
    item => item.formId === formItemId,
  );

  return formIndex !== -1;
};

export const updateFormListData = (
  keyName: string[],
  formItemId: number,
  updater: Updater<FormItemResponse>,
) => {
  queryClient.setQueryData(
    keyName,
    (oldData: InfiniteData<AxiosResponse<FormListResponse>>) => {
      if (!oldData?.pages.length) return oldData;

      const [pageIndex, formIndex] = findIndexOnPages<FormItemResponse>(
        oldData.pages,
        item => item.formId === formItemId,
      );

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

      return oldData;
    },
  );
};

export const setFormDetailData = (
  formItemId: number,
  updater: Updater<ChatListItem>,
) => {
  queryClient.setQueryData(
    formKeys.detail(formItemId),
    (oldData: AxiosResponse<ChatByIdResponse>) => {
      if (!oldData) return oldData;

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

      return oldData;
    },
  );
};

export const clearFormDetailUnreadMessages = (id: number) => {
  setFormDetailData(id, (chatItem: ChatListItem) => {
    if (!chatItem?.chat) return chatItem;

    chatItem.chat.unreadMessages = 0;

    return chatItem;
  });
};

export const setFormMessageListData = (
  formItemId: number,
  updater: Updater<ChatMessagesResponse>,
  pageNumber: number = 0,
) => {
  queryClient.setQueryData(
    formKeys.messages.list(formItemId),
    (oldData: any) => {
      if (!oldData) return oldData;

      oldData.pages[pageNumber].data = functionalUpdate(
        updater,
        oldData.pages[pageNumber].data,
      );

      return oldData;
    },
  );
};

export const checkAssignFormToAgent = async (
  formId,
  segmentationItemId,
  usersIds,
  userId,
  callback,
) => {
  try {
    const response = await getUserSegmentations(userId);
    if (response?.data) {
      if (
        usersIds.includes(userId) ||
        response?.data
          .map(segmentation => segmentation.item.id)
          .includes(segmentationItemId)
      ) {
        queryClient.invalidateQueries(
          formKeys.formApprovalWorkflow(Number(formId)),
        );
        queryClient.invalidateQueries(formKeys.unassignedForms());
      }
    }
  } catch (e) {
    callback();
  }
};

export const pushFormDetailMessage = (
  formItemId: number,
  newMessage: Message,
) => {
  setFormMessageListData(formItemId, oldData => {
    if (!oldData) return oldData;

    oldData.items = [newMessage, ...(oldData.items || [])];

    return oldData;
  });
};

export const replaceFormDetailMessage = (
  formItemId: number,
  messageId: string | number,
  newMessage: Message,
) => {
  setFormDetailMessage(formItemId, messageId, oldDetail => {
    if (!oldDetail) return oldDetail;
    oldDetail = newMessage;
    return oldDetail;
  });
};

export const getUnreadFormChats = (): number[] => {
  const formListData: InfiniteData<AxiosResponse<FormPaginationResponse>> =
    queryClient.getQueryData(formKeys.listCompleted(FormType.FORM));

  const results = formListData?.pages?.flatMap(page => page.data.items);

  if (!results?.length) return [];

  const listformsUnread = results
    .filter(formItem => formItem?.unreadMessages > 0)
    .map(elem => elem.chatId);

  return listformsUnread;
};

export const isFormChat = (id: number): boolean => {
  const formListData: InfiniteData<AxiosResponse<FormPaginationResponse>> =
    queryClient.getQueryData(formKeys.listCompleted(FormType.FORM));

  if (!formListData) return;

  const formPage = formListData?.pages.findIndex(element =>
    element.data.items.find(item => item.chatId === id),
  );

  if (formPage === -1) return;

  const formItemsIndex = formListData?.pages[formPage].data?.items?.findIndex(
    item => item.chatId === id,
  );

  if (formItemsIndex === -1) return;

  return !formListData.pages[formPage].data.items[formItemsIndex].needApproval;
};

export const setFormCompletedListData = (
  formItemChatId: number,
  updater: Updater<FormItemResponse>,
  numberPage = 0,
) => {
  queryClient.setQueryData(
    formKeys.listCompleted(FormType.FORM),
    (oldData: InfiniteData<AxiosResponse<FormPaginationResponse>>) => {
      if (!oldData) return oldData;

      const formItemsIndex = oldData.pages[numberPage].data.items.findIndex(
        item => item.chatId === formItemChatId,
      );

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

      return oldData;
    },
  );
};

export const setFormDetailMessage = (
  formItemId: number,
  messageId: string | number,
  updater: Updater<Message>,
) => {
  queryClient.setQueryData(
    formKeys.messages.list(formItemId),
    (oldData: any) => {
      if (!oldData) return oldData;

      const [pageIndex, messageIndex] = findIndexOnPaginatedPages<Message>(
        oldData.pages,
        item => item.id === messageId,
      );

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

      return oldData;
    },
  );
};

export const setFormData = (
  formItemId: number,
  updater: Updater<FormItemResponse>,
) => {
  queryClient.setQueryData(formKeys.assignedForms(), (oldData: any) => {
    if (!oldData) return oldData;

    const [pageIndex, formIndex] = findFormIndexOnPages(
      formItemId,
      oldData.pages,
    );

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

    return oldData;
  });
};

export const moveFirstFormRequestListData = (
  formItemChatId: number,
  numberPage = 0,
) => {
  queryClient.setQueryData(
    formKeys.assignedForms(),
    (oldData: InfiniteData<AxiosResponse<FormListResponse>>) => {
      if (!oldData) return oldData;

      const formItem = oldData.pages[numberPage].data.find(
        item => item.chatId === formItemChatId,
      );

      if (formItem) {
        oldData.pages[numberPage].data = [
          formItem,
          ...oldData.pages[numberPage].data.filter(
            item => item.chatId !== formItem.chatId,
          ),
        ];
      }
      return oldData;
    },
  );
};

export const moveFirstFormChatListData = (
  chatItemId: number,
  numberPage = 0,
) => {
  queryClient.setQueryData(
    formKeys.listCompleted(FormType.FORM),
    (oldData: InfiniteData<AxiosResponse<FormPaginationResponse>>) => {
      if (!oldData) return oldData;

      const formItem = oldData.pages[numberPage].data.items.find(
        item => item.chatId === chatItemId,
      );

      if (formItem) {
        oldData.pages[numberPage].data.items = [
          formItem,
          ...oldData.pages[numberPage].data.items.filter(
            item => item.chatId !== chatItemId,
          ),
        ];
      }

      return oldData;
    },
  );
};

export const addFormMessageDataReaction = (
  formItemId: number,
  messageId: string | number,
  emoji: string,
  unified: string,
  isLoggedUserReaction: boolean = true,
) => {
  setFormDetailMessage(formItemId, messageId, oldMessage => {
    if (!oldMessage) return oldMessage;

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

    sortReactions(oldMessage.reactions);

    return oldMessage;
  });
};

export const removeFormMessageDataReaction = (
  formItemId: number,
  messageId: string | number,
  emoji: string,
  isLoggedUserReaction: boolean = true,
) => {
  setFormDetailMessage(formItemId, messageId, oldMessage => {
    if (!oldMessage) return oldMessage;

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

    sortReactions(oldMessage.reactions);

    return oldMessage;
  });
};

export const addStepPreSubmit = id => {
  queryClient.setQueryData(formKeys.description(id), (oldData: any) => {
    if (!oldData) return oldData;

    oldData.data.steps = [...oldData.data.steps, makePreSubmitStep()];

    return oldData;
  });
};

export const deleteFormWithSectionBracing = id => {
  queryClient.removeQueries(formKeys.description(id), { exact: true });
};

// Notifications

export const addFormChatNotification = (count: number = 1) => {
  queryClient.setQueryData(
    formKeys.notifications.module(),
    (oldData: AxiosResponse<FormChatsNotificationsResponse>) => {
      if (!oldData?.data) return oldData;

      oldData.data.unreadUserFormChatNotifications += count;

      return oldData;
    },
  );
};

export const removeFormChatNotification = (count: number = 1) => {
  queryClient.setQueryData(
    formKeys.notifications.module(),
    (oldData: AxiosResponse<FormChatsNotificationsResponse>) => {
      if (!oldData?.data) return oldData;

      oldData.data.unreadUserFormChatNotifications -= count;

      return oldData;
    },
  );
};

export const addFormRequestNotification = (count: number = 1) => {
  queryClient.setQueryData(
    formKeys.notifications.module(),
    (oldData: AxiosResponse<FormChatsNotificationsResponse>) => {
      if (!oldData?.data) return oldData;

      oldData.data.unreadAgentFormChatNotifications += count;

      return oldData;
    },
  );
};

export const removeFormRequestNotification = (count: number = 1) => {
  queryClient.setQueryData(
    formKeys.notifications.module(),
    (oldData: AxiosResponse<FormChatsNotificationsResponse>) => {
      if (!oldData?.data) return oldData;

      oldData.data.unreadAgentFormChatNotifications -= count;

      return oldData;
    },
  );
};

export const invalidateFormsNotifications = () => {
  queryClient.invalidateQueries(formKeys.notifications.module());
};

export const updateStaticsListCompleted = () => {
  queryClient.invalidateQueries(formKeys.stats());
};

export const isApprovalWorkflow = (formId: number) => {
  const formData: AxiosResponse<FormListItem> = queryClient.getQueryData(
    formKeys.formApprovalWorkflow(Number(formId)),
  );

  return !!formData?.data?.id;
};

export const updateFormsNotifications = (form = undefined) => {
  queryClient.invalidateQueries(formKeys.stats());
  queryClient.invalidateQueries(formKeys.listCompleted(FormType.FORM));
  queryClient.invalidateQueries(formKeys.unassignedForms());
  queryClient.invalidateQueries(formKeys.assignedForms());
  queryClient.invalidateQueries(formKeys.redBubblesAgent());
  if (form?.formId && isApprovalWorkflow(form?.formId)) {
    queryClient.invalidateQueries(
      formKeys.formApprovalWorkflow(Number(form?.formId)),
    );
  }
  invalidateFormsNotifications();
};

export const updateFormsChatNotifications = (chatId: number) => {
  queryClient.invalidateQueries(formKeys.stats());
  queryClient.invalidateQueries(formKeys.listCompleted(FormType.FORM));
  queryClient.invalidateQueries(chatKeys.detail(chatId));
  queryClient.invalidateQueries(chatKeys.messages.list(chatId));
  invalidateFormsNotifications();
};

export const forEachFormDetailMessage = (
  chatItemId: number,
  updater: Updater<Message>,
) => {
  queryClient.setQueryData(
    formKeys.messages.list(chatItemId),
    (oldData: any) => {
      if (!oldData) return oldData;

      oldData?.pages?.forEach(page =>
        page?.data?.items?.forEach(message =>
          functionalUpdate(updater, message),
        ),
      );

      return oldData;
    },
  );
};

export const editFormMessage = data => {
  const { id, chatId } = data;

  forEachFormDetailMessage(Number(chatId), oldMessage => {
    if (!oldMessage) return oldMessage;

    if (oldMessage.id === id) {
      updateMessage(oldMessage, data);
    } else if (oldMessage.replyMessageId === id) {
      updateMessage(oldMessage.replyMessage, data);
    }

    return oldMessage;
  });
};
