import {
  createContext,
  type FC,
  type ReactNode,
  useContext,
  useEffect,
} from 'react';
import { useQueryClient } from 'react-query';
import { matchPath, useNavigate } from 'react-router-dom';

import useSnackbar from '@material-hu/components/design-system/Snackbar';

import { encryptStorage } from 'src/config/encrypt-storage';
import { logEvent } from 'src/config/logging';
import { type ExtendedCallback, SocketConnection } from 'src/config/socket';
import { EVENTS_SOCKETS } from 'src/constants/sockets';
import { useAuth } from 'src/contexts/JWTContext';
import { useSettings } from 'src/contexts/SettingsContext';
import useChat from 'src/hooks/useChat';
import useFeatureFlag from 'src/hooks/useFeatureFlag';
import useForm from 'src/hooks/useForm';
import useUnreadChats from 'src/hooks/useUnreadChats';
import useHandlerEvents from 'src/pages/dashboard/Conversations/hooks/useHandlerEvents';
import {
  dropPendingDocumentListData,
  pushPendingDocumentListData,
  setPendingDocumentListData,
  updateDocuments,
} from 'src/pages/dashboard/documents/queries';
import {
  dropChildComment,
  dropComment,
  invalidateLivestreamPollData,
  postUpdated,
  unshiftChildComment,
  unshiftComment,
  updateChildComment,
  updateComment as updateCommentCacheUpdate,
  updateReactionChildCommentData,
  updateReactionCommentData,
} from 'src/pages/dashboard/feed/queries';
import {
  invalidateGroupDetails,
  invalidateGroupList,
  invalidateGroupNotifications,
  invalidateGroupPost,
  invalidateLivestreamGroupPollData,
} from 'src/pages/dashboard/groups/queries';
import {
  invalidateDeletedLibrary,
  updateLibraryStatus,
} from 'src/pages/dashboard/libraries/queries';
import { librariesRoutes } from 'src/pages/dashboard/libraries/routes';
import {
  marketplaceKeys,
  updateMarketplacePostReaction,
} from 'src/pages/dashboard/marketplace/queries';
import { clearNewsNotifications } from 'src/pages/dashboard/news/queries';
import { addArticle, updateArticle } from 'src/pages/dashboard/news/sockets';
import {
  addOnboarding,
  deleteOnboarding,
} from 'src/pages/dashboard/onboarding/queries';
import { serviceManagementKeys } from 'src/pages/dashboard/serviceManagement/queries';
import { useTranslation } from 'src/pages/dashboard/tickets/i18n';
import {
  getTicket,
  isTicketOfTopic,
  updateAgentTickets,
} from 'src/pages/dashboard/tickets/queries';
import {
  ticketRoutes,
  ticketSkeletonRoutes,
} from 'src/pages/dashboard/tickets/routes';
import {
  addTicket,
  chatMessageAgent,
  deleteTicket,
  deleteTopic,
  updateTopic,
  updateTopicOfTicket,
} from 'src/pages/dashboard/tickets/sockets';
import { handleCategorizedHourStatusUpdated } from 'src/pages/dashboard/timeTracking/sockets';
import {
  invalidateActiveVideoCall,
  invalidateParticipantList,
  invalidateVideoCall,
} from 'src/pages/dashboard/videoCall/queries';
import { EventName } from 'src/types/amplitude';
import { type BubbleCode } from 'src/types/bubbaloo';
import { type Message } from 'src/types/chats';
import { type Comment } from 'src/types/comments';
import { type Document } from 'src/types/documents';
import { FeatureFlags } from 'src/types/featureFlags';
import { type UpdateFormChatAssigment } from 'src/types/forms';
import { type KnowledgeLibraryDeletedSocket } from 'src/types/libraries';
import { type MarketplacePostReactionSocket } from 'src/types/marketplace';
import {
  type ClearNotificationsSocket,
  RedBubbles,
} from 'src/pages/dashboard/notifications/types';
import {
  type GroupPostHlsStreamSocket,
  type GroupPostStreamSocket,
  type Post,
  type PostCommentCreatedSocket,
  type PostCommentDeletedSocket,
  type PostCommentReactionSocket,
  type PostHlsStreamSocket,
  type PostStreamSocket,
} from 'src/types/posts';
import {
  type CallEvent,
  type CallRefreshParticipants,
  type PollUpdatedEvent,
} from 'src/types/stream';
import {
  type ChatMessageAgentSocket,
  type CreateTicketSocket,
  type DeleteUnassignedTicketSocket,
  type TicketTopicDeletedSocket,
  type TicketTopicUpdatedSocket,
  type TopicOfTicketUpdatedSocket,
} from 'src/types/tickets';
import { type User, type UserDeletedSocket } from 'src/types/user';
import { isPendingDocument } from 'src/utils/documents';
import { updateGeneralRedBubbles } from 'src/utils/generalRedBubbles';
import { userIsOnGroupPage } from 'src/utils/groups';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { addReaction, removeReaction } from 'src/utils/reactions';
import { isTicketResponsible } from 'src/utils/tickets';

import { chatKeys } from 'src/components/dashboard/chat/queries';
import {
  updateFormsChatNotifications,
  updateFormsNotifications,
} from 'src/components/dashboard/form/queries';
import {
  deleteSurvey,
  disabledSurvey,
  enabledSurvey,
  updateRemainingAttemps,
} from 'src/components/dashboard/surveys/sockets';
import { deleteUser } from 'src/components/dashboard/users/sockets';
import {
  dropWidget,
  pushWidget,
  updateWidget,
  updateWidgetList,
} from 'src/components/dashboard/widgets/queries';

type SocketProviderProps = {
  children?: ReactNode;
};

export type SocketContextValue = {
  startSocket: (environment: string, token: string) => void;
  listenEvent: (event: string, cb: ExtendedCallback) => void;
  listenEventList: (eventList: string[], cb: ExtendedCallback) => void;
  emitEvent: (event: string, data: Record<string, unknown>) => void;
  emitNotifyEvent: (event: string, data: Record<string, unknown>) => void;
  closeEvent: (event: string, cb: ExtendedCallback) => void;
  closeEventList: (eventList: string[], cb: ExtendedCallback) => void;
  lastUpdated?: string;
};

const SocketContext = createContext<SocketContextValue>(new SocketConnection());

const socket = new SocketConnection();

export const SocketProvider: FC<
  React.PropsWithChildren<SocketProviderProps>
> = props => {
  const { children } = props;

  const { t } = useTranslation();
  const { t: tUsers } = useLokaliseTranslation('users');

  const { enqueueSnackbar } = useSnackbar();
  const queryClient = useQueryClient();
  const navigate = useNavigate();
  const unread = useUnreadChats();

  const {
    user,
    logout,
    updateUserPermissions,
    updateStaticProfileSettings,
    updateInstancePermissions,
    updateInstance,
    updateAcknowledgementsLeftToGive,
    changeLogoInstance,
    updateUserRolesAndPermissions,
  } = useAuth();

  const { setPrimary } = useSettings();

  const {
    updateChatNotifications,
    addUnreadChat,
    addMessage,
    editMessage,
    markAsUnread,
    updateStatus,
    updateGroupChatPictureChat,
    updateGroupChatTitleChat,
  } = useChat();

  const {
    resetMarkedUnread,
    handleUpdateMessage,
    handleDeletedMessage,
    handleUpdateReadStatus,
    handleUpdateGroupInfo,
    handleReaction,
    handleUptateGroup,
    handleAddMessage,
    handleUpdatePinnedMessages,
    handleAddPinnedMessage,
    handleUpdateMessageReadByAll,
  } = useHandlerEvents();

  const isCerberusEnabled = useFeatureFlag(FeatureFlags.CERBERUS_ENABLED);

  const { addFormMessage, closeFormChat, addFormRequestMessage, deleteForm } =
    useForm();

  useEffect(() => {
    socket.startSocket();

    return () => socket && socket.stopSocket();
  }, []);

  // Auth
  useEffect(() => {
    const updateLogoOrColor = (params: { color: string; logo: string }) => {
      if (params.color) setPrimary(params.color);
      if (params.logo) changeLogoInstance?.(params.logo);
    };

    const handleValidSessions = ({ sessionIds }: { sessionIds: string[] }) => {
      const currentSession = encryptStorage.getItem('sessionId');
      if (!sessionIds.includes(currentSession)) {
        enqueueSnackbar({
          title: tUsers('WARNING_SESSION_KICKED'),
          variant: 'warning',
        });
        logout?.();
      }
    };

    const handleLogoutOnAnotherDevice = ({
      sessionId,
    }: {
      sessionId: string;
    }) => {
      const currentSession = encryptStorage.getItem('sessionId');
      if (sessionId === currentSession) {
        enqueueSnackbar({
          title: tUsers('WARNING_SESSION_KICKED'),
          variant: 'warning',
        });
        logout?.();
      }
    };

    socket.listenEvent(
      EVENTS_SOCKETS.CURRENT_VALID_SESSIONS,
      handleValidSessions,
    );
    socket.listenEvent(EVENTS_SOCKETS.LOGOUT, handleLogoutOnAnotherDevice);

    if (updateStaticProfileSettings) {
      socket.listenEvent(
        EVENTS_SOCKETS.STATIC_PROFILE_FIELD_SETTINGS_UPDATED,
        updateStaticProfileSettings,
      );
    }
    if (updateUserPermissions) {
      socket.listenEvent(
        EVENTS_SOCKETS.UPDATED_USER_CAPABILITIES,
        updateUserPermissions,
      );
    }
    if (updateInstancePermissions) {
      socket.listenEvent(
        EVENTS_SOCKETS.UPDATED_INSTANCE_CAPABILITIES,
        updateInstancePermissions,
      );
    }
    if (updateInstance) {
      socket.listenEvent(EVENTS_SOCKETS.UPDATED_INSTANCE, updateInstance);
    }

    socket.listenEvent(EVENTS_SOCKETS.LOGO_UPDATED, updateLogoOrColor);

    if (isCerberusEnabled && updateUserRolesAndPermissions) {
      socket.listenEvent(
        EVENTS_SOCKETS.USER_PERMISSIONS_CHANGED,
        updateUserRolesAndPermissions,
      );
    }

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.CURRENT_VALID_SESSIONS,
        handleValidSessions,
      );

      socket.closeEvent(EVENTS_SOCKETS.LOGOUT, handleLogoutOnAnotherDevice);

      if (updateStaticProfileSettings) {
        socket.closeEvent(
          EVENTS_SOCKETS.STATIC_PROFILE_FIELD_SETTINGS_UPDATED,
          updateStaticProfileSettings,
        );
      }
      if (updateUserPermissions) {
        socket.closeEvent(
          EVENTS_SOCKETS.UPDATED_USER_CAPABILITIES,
          updateUserPermissions,
        );
      }
      if (updateInstancePermissions) {
        socket.closeEvent(
          EVENTS_SOCKETS.UPDATED_INSTANCE_CAPABILITIES,
          updateInstancePermissions,
        );
      }
      if (updateInstance) {
        socket.closeEvent(EVENTS_SOCKETS.UPDATED_INSTANCE, updateInstance);
      }

      socket.closeEvent(EVENTS_SOCKETS.LOGO_UPDATED, updateLogoOrColor);

      if (isCerberusEnabled && updateUserRolesAndPermissions) {
        socket.closeEvent(
          EVENTS_SOCKETS.USER_PERMISSIONS_CHANGED,
          updateUserRolesAndPermissions,
        );
      }
    };
  }, [
    updateUserPermissions,
    updateInstancePermissions,
    isCerberusEnabled,
    updateUserRolesAndPermissions,
  ]);

  // Posts
  useEffect(() => {
    const updatePost = (post: Post) => {
      postUpdated(post?.id);
    };

    const updatePostStream = (postStream: PostStreamSocket) => {
      postUpdated(postStream?.postId);
    };

    const updateGroupPostStream = (postStream: GroupPostStreamSocket) => {
      invalidateGroupPost(postStream.groupId, postStream.postId);
    };

    const updateHlsPostStream = (hlsPostStream: PostHlsStreamSocket) => {
      postUpdated(hlsPostStream?.postId);
    };

    const updateHlsGroupPostStream = (
      hlsPostStream: GroupPostHlsStreamSocket,
    ) => {
      invalidateGroupPost(hlsPostStream.groupId, hlsPostStream.postId);
    };

    const addNewComment = (data: PostCommentCreatedSocket) => {
      const { postId, ...comment } = data;

      comment.parentId !== null
        ? unshiftChildComment(postId, comment.parentId, comment)
        : unshiftComment(postId, comment);
    };

    const updateComment = (data: { postId: number } & Comment) => {
      const { postId, ...comment } = data;

      comment.parentId !== null
        ? updateChildComment(postId, comment.parentId, comment)
        : updateCommentCacheUpdate(postId, comment);
    };

    const removeComment = (data: PostCommentDeletedSocket) => {
      const { parentId, commentId, postId } = data;

      parentId !== null
        ? dropChildComment(postId, parentId, commentId)
        : dropComment(postId, commentId);
    };

    const addCommentReaction = (reaction: PostCommentReactionSocket) => {
      const { emoji, unified, userId, commentId, postId, parentId } = reaction;

      parentId !== null
        ? updateReactionChildCommentData(
            postId,
            parentId,
            commentId,
            reactions =>
              addReaction(reactions, emoji, unified, userId === user?.id),
          )
        : updateReactionCommentData(postId, commentId, reactions =>
            addReaction(reactions, emoji, unified, userId === user?.id),
          );
    };

    const removeCommentReaction = (reaction: PostCommentReactionSocket) => {
      const { emoji, userId, commentId, postId, parentId } = reaction;

      parentId !== null
        ? updateReactionChildCommentData(
            postId,
            parentId,
            commentId,
            reactions => removeReaction(reactions, emoji, userId === user?.id),
          )
        : updateReactionCommentData(postId, commentId, reactions =>
            removeReaction(reactions, emoji, userId === user?.id),
          );
    };

    const updateLivestreamPoll = ({ livestreamId }: PollUpdatedEvent) => {
      invalidateLivestreamPollData(livestreamId);
      invalidateLivestreamGroupPollData(livestreamId);
    };

    socket.listenEvent(EVENTS_SOCKETS.POST_UPDATED, updatePost);
    socket.listenEvent(EVENTS_SOCKETS.POST_COMMENT_CREATED, addNewComment);
    socket.listenEvent(EVENTS_SOCKETS.POST_COMMENT_UPDATED, updateComment);
    socket.listenEvent(EVENTS_SOCKETS.POST_COMMENT_DELETED, removeComment);
    socket.listenEvent(
      EVENTS_SOCKETS.NEW_POST_COMMENT_REACTION,
      addCommentReaction,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.REMOVE_POST_COMMENT_REACTION,
      removeCommentReaction,
    );
    socket.listenEvent(EVENTS_SOCKETS.LIVESTREAM_RECORDED, updatePostStream);
    socket.listenEvent(
      EVENTS_SOCKETS.LIVESTREAM_CAPTIONS_ENABLED,
      updatePostStream,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.GROUP_POST_LIVESTREAM_RECORDED,
      updateGroupPostStream,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.LIVESTREAM_HLS_STARTED,
      updateHlsPostStream,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.GROUP_POST_LIVESTREAM_HLS_STARTED,
      updateHlsGroupPostStream,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.GROUP_POST_LIVESTREAM_CAPTIONS_ENABLED,
      updateGroupPostStream,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.LIVESTREAM_POLL_INITIALIZED,
      updateLivestreamPoll,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.LIVESTREAM_POLL_UPDATED,
      updateLivestreamPoll,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.LIVESTREAM_POLL_DELETED,
      updateLivestreamPoll,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.LIVESTREAM_POLL_SHOW_FINAL_RESULT_UPDATED,
      updateLivestreamPoll,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.LIVESTREAM_POLL_ENDED,
      updateLivestreamPoll,
    );

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.POST_UPDATED, updatePost);
      socket.closeEvent(EVENTS_SOCKETS.POST_COMMENT_CREATED, addNewComment);
      socket.closeEvent(EVENTS_SOCKETS.POST_COMMENT_UPDATED, updateComment);
      socket.closeEvent(EVENTS_SOCKETS.POST_COMMENT_DELETED, removeComment);
      socket.closeEvent(
        EVENTS_SOCKETS.NEW_POST_COMMENT_REACTION,
        addCommentReaction,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.REMOVE_POST_COMMENT_REACTION,
        removeCommentReaction,
      );
      socket.closeEvent(EVENTS_SOCKETS.LIVESTREAM_RECORDED, updatePostStream);
      socket.closeEvent(
        EVENTS_SOCKETS.LIVESTREAM_CAPTIONS_ENABLED,
        updatePostStream,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.GROUP_POST_LIVESTREAM_RECORDED,
        updateGroupPostStream,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.LIVESTREAM_HLS_STARTED,
        updateHlsPostStream,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.GROUP_POST_LIVESTREAM_HLS_STARTED,
        updateHlsGroupPostStream,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.GROUP_POST_LIVESTREAM_CAPTIONS_ENABLED,
        updateGroupPostStream,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.LIVESTREAM_POLL_INITIALIZED,
        updateLivestreamPoll,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.LIVESTREAM_POLL_UPDATED,
        updateLivestreamPoll,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.LIVESTREAM_POLL_DELETED,
        updateLivestreamPoll,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.LIVESTREAM_POLL_SHOW_FINAL_RESULT_UPDATED,
        updateLivestreamPoll,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.LIVESTREAM_POLL_ENDED,
        updateLivestreamPoll,
      );
    };
  }, []);

  // GROUPS
  useEffect(() => {
    const addNewGroup = () => invalidateGroupList();

    const removeGroup = ({ groupId }: { groupId: number }) => {
      const isOnRemovedGroup = userIsOnGroupPage(groupId);
      if (isOnRemovedGroup) navigate('/');
      invalidateGroupList();
    };

    const updateGroup = ({ id }: { id: number }) => {
      const isOnUpdatedGroup = userIsOnGroupPage(id);
      if (isOnUpdatedGroup) invalidateGroupDetails(id);
      invalidateGroupList();
    };

    const updateGroupNotifications = ({
      groupId,
      user: author,
    }: {
      groupId: number;
      user: User;
    }) => {
      const isOnUpdatedGroup = userIsOnGroupPage(groupId);
      const isPostAuthor = user?.id === author.id;
      if (!isOnUpdatedGroup && !isPostAuthor) {
        invalidateGroupNotifications();
        invalidateGroupList();
      }
    };

    socket.listenEvent(EVENTS_SOCKETS.GROUP_CREATED, addNewGroup);
    socket.listenEvent(EVENTS_SOCKETS.GROUP_UPDATED, updateGroup);
    socket.listenEvent(EVENTS_SOCKETS.GROUP_DELETED, removeGroup);
    socket.listenEvent(
      EVENTS_SOCKETS.GROUP_POST_CREATED,
      updateGroupNotifications,
    );

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.GROUP_CREATED, addNewGroup);
      socket.closeEvent(EVENTS_SOCKETS.GROUP_UPDATED, updateGroup);
      socket.closeEvent(EVENTS_SOCKETS.GROUP_DELETED, removeGroup);
      socket.closeEvent(
        EVENTS_SOCKETS.GROUP_POST_CREATED,
        updateGroupNotifications,
      );
    };
  }, []);

  // MARKETPLACE
  useEffect(() => {
    const addPost = (post: Post) => {
      queryClient.invalidateQueries(marketplaceKeys.list());
      if (user?.id === post?.user?.id) {
        queryClient.invalidateQueries(marketplaceKeys.mine());
        queryClient.invalidateQueries(marketplaceKeys.mineCount());
      }
      queryClient.invalidateQueries(marketplaceKeys.notifications.module());
    };

    const dropPost = () => {
      queryClient.invalidateQueries(marketplaceKeys.list());
      queryClient.invalidateQueries(marketplaceKeys.mine());
      queryClient.invalidateQueries(marketplaceKeys.mineCount());
      queryClient.invalidateQueries(marketplaceKeys.notifications.module());
    };

    const updatePost = (post: Post) => {
      queryClient.invalidateQueries(marketplaceKeys.list());
      if (user?.id === post?.user?.id) {
        queryClient.invalidateQueries(marketplaceKeys.mine());
        queryClient.invalidateQueries(marketplaceKeys.mineCount());
      }
      queryClient.invalidateQueries(marketplaceKeys.detail(post?.id));
    };

    const addPostReaction = (reaction: MarketplacePostReactionSocket) => {
      const { emoji, unified, userId, marketplacePostId } = reaction;

      updateMarketplacePostReaction(marketplacePostId, emoji, reactions =>
        addReaction(reactions, emoji, unified, userId === user?.id),
      );
    };

    const removePostReaction = (reaction: MarketplacePostReactionSocket) => {
      const { emoji, userId, marketplacePostId } = reaction;

      updateMarketplacePostReaction(marketplacePostId, emoji, reactions =>
        removeReaction(reactions, emoji, userId === user?.id),
      );
    };

    socket.listenEvent(EVENTS_SOCKETS.MARKETPLACE_POST_CREATED, addPost);
    socket.listenEvent(EVENTS_SOCKETS.MARKETPLACE_POST_UPDATED, updatePost);
    socket.listenEvent(EVENTS_SOCKETS.MARKETPLACE_POST_DELETED, dropPost);
    socket.listenEvent(
      EVENTS_SOCKETS.NEW_MARKETPLACE_POST_REACTION,
      addPostReaction,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.REMOVE_MARKETPLACE_POST_REACTION,
      removePostReaction,
    );

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.MARKETPLACE_POST_CREATED, addPost);
      socket.closeEvent(EVENTS_SOCKETS.MARKETPLACE_POST_UPDATED, updatePost);
      socket.closeEvent(EVENTS_SOCKETS.MARKETPLACE_POST_DELETED, dropPost);
      socket.closeEvent(
        EVENTS_SOCKETS.NEW_MARKETPLACE_POST_REACTION,
        addPostReaction,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.REMOVE_MARKETPLACE_POST_REACTION,
        removePostReaction,
      );
    };
  }, [socket]);

  // Forms
  useEffect(() => {
    const updateFormChatAssigment = (form: UpdateFormChatAssigment) => {
      updateFormsChatNotifications(form?.chat?.id);
    };

    const newFormChatCreated = () => {
      updateFormsNotifications();
      logEvent(EventName.NEW_FORM_CHAT_CREATED_BUBBLES);
    };
    const approvalFormTransitioned = (form = undefined) => {
      updateFormsNotifications(form);
      logEvent(EventName.APPROVAL_FORM_TRANSITIONED_BUBBLES);
    };
    const approvalFormClosed = (form = undefined) => {
      updateFormsNotifications(form);
      logEvent(EventName.APPROVAL_FORM_CLOSED_BUBBLES);
    };
    const newAssigmentOfFormAgentBubbles = () => {
      updateFormsNotifications();
      logEvent(EventName.NEW_ASSIGMENT_OF_FORM_AGENT_BUBBLES);
    };
    const formApprovalStepAssigned = () => {
      updateFormsNotifications();
      logEvent(EventName.FORM_APPROVAL_STEP_ASSIGNED_BUBBLES);
    };
    const deleteUnassignedFormChat = () => {
      updateFormsNotifications();
      logEvent(EventName.DELETE_UNASSIGNED_FORM_CHAT_BUBBLES);
    };
    const addedSubordinate = () => {
      updateFormsNotifications();
      logEvent(EventName.ADDED_SUBORDINATE_BUBBLES);
    };
    const removedSubordinate = () => {
      updateFormsNotifications();
      logEvent(EventName.REMOVED_SUBORDINATE_BUBBLES);
    };
    const approvalWorkflowStepResponsibleAdded = () => {
      updateFormsNotifications();
      logEvent(EventName.APPROVAL_WORKFLOW_STEP_RESPONSIBLE_ADDED_BUBBLES);
    };
    const approvalWorkflowStepResponsibleRemoved = () => {
      updateFormsNotifications();
      logEvent(EventName.APPROVAL_WORKFLOW_STEP_RESPONSIBLE_REMOVED_BUBBLES);
    };

    socket.listenEvent(EVENTS_SOCKETS.FORM_DELETED, deleteForm);
    socket.listenEvent(EVENTS_SOCKETS.GROUP_CHAT_USERS_ADDED, updateStatus);
    socket.listenEvent(EVENTS_SOCKETS.GROUP_CHAT_USERS_REMOVED, updateStatus);
    socket.listenEvent(
      EVENTS_SOCKETS.GROUP_CHAT_TITLE_CHANGED,
      updateGroupChatTitleChat,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.GROUP_CHAT_PICTURE_CHANGED,
      updateGroupChatPictureChat,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.NEW_FORM_CHAT_MESSAGE_USER,
      addFormMessage,
    );
    socket.listenEvent(EVENTS_SOCKETS.FORM_CHAT_CLOSED, closeFormChat);
    socket.listenEvent(
      EVENTS_SOCKETS.NEW_FORM_CHAT_MESSAGE_AGENT,
      addFormRequestMessage,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.ONBOARDING_USER_TASK_CREATED,
      addOnboarding,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.ONBOARDING_USER_TASK_DELETED,
      deleteOnboarding,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.ONBOARDING_USER_TASK_UPDATED,
      deleteOnboarding,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.ONBOARDING_TASK_DELETED,
      deleteOnboarding,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.ONBOARDING_CATEGORY_DELETED,
      deleteOnboarding,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.NEW_FORM_CHAT_CREATED,
      newFormChatCreated,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.APPROVAL_FORM_TRANSITIONED,
      approvalFormTransitioned,
    );
    socket.listenEvent(EVENTS_SOCKETS.APPROVAL_FORM_CLOSED, approvalFormClosed);
    socket.listenEvent(
      EVENTS_SOCKETS.NEW_ASSIGMENT_OF_FORM_AGENT,
      newAssigmentOfFormAgentBubbles,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.FORM_APPROVAL_STEP_ASSIGNED,
      formApprovalStepAssigned,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.DELETE_UNASSIGNED_FORM_CHAT,
      deleteUnassignedFormChat,
    );
    socket.listenEvent(EVENTS_SOCKETS.ADDED_SUBORDINATE, addedSubordinate);
    socket.listenEvent(EVENTS_SOCKETS.REMOVED_SUBORDINATE, removedSubordinate);
    socket.listenEvent(
      EVENTS_SOCKETS.APPROVAL_WORKFLOW_STEP_RESPONSIBLE_ADDED,
      approvalWorkflowStepResponsibleAdded,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.APPROVAL_WORKFLOW_STEP_RESPONSIBLE_REMOVED,
      approvalWorkflowStepResponsibleRemoved,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.UPDATE_FORM_CHAT_ASSIGNMENT,
      updateFormChatAssigment,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.USER_SEGMENTATION_UPDATED,
      updateGeneralRedBubbles,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.ADDED_TO_SEGMENTATION_ITEM,
      updateGeneralRedBubbles,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.REMOVED_FROM_SEGMENTATION_ITEM,
      updateGeneralRedBubbles,
    );

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.FORM_DELETED, deleteForm);
      socket.closeEvent(EVENTS_SOCKETS.GROUP_CHAT_USERS_ADDED, updateStatus);
      socket.closeEvent(EVENTS_SOCKETS.GROUP_CHAT_USERS_REMOVED, updateStatus);
      socket.closeEvent(
        EVENTS_SOCKETS.GROUP_CHAT_TITLE_CHANGED,
        updateGroupChatTitleChat,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.GROUP_CHAT_PICTURE_CHANGED,
        updateGroupChatPictureChat,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.NEW_FORM_CHAT_MESSAGE_USER,
        addFormMessage,
      );
      socket.closeEvent(EVENTS_SOCKETS.FORM_CHAT_CLOSED, closeFormChat);
      socket.closeEvent(
        EVENTS_SOCKETS.NEW_FORM_CHAT_MESSAGE_AGENT,
        addFormRequestMessage,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.ONBOARDING_USER_TASK_CREATED,
        addOnboarding,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.ONBOARDING_USER_TASK_DELETED,
        deleteOnboarding,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.ONBOARDING_USER_TASK_UPDATED,
        deleteOnboarding,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.ONBOARDING_TASK_DELETED,
        deleteOnboarding,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.ONBOARDING_CATEGORY_DELETED,
        deleteOnboarding,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.NEW_FORM_CHAT_CREATED,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.APPROVAL_FORM_TRANSITIONED,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.APPROVAL_FORM_CLOSED,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.NEW_ASSIGMENT_OF_FORM_AGENT,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.FORM_APPROVAL_STEP_ASSIGNED,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.DELETE_UNASSIGNED_FORM_CHAT,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.ADDED_SUBORDINATE,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.REMOVED_SUBORDINATE,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.APPROVAL_WORKFLOW_STEP_RESPONSIBLE_ADDED,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.APPROVAL_WORKFLOW_STEP_RESPONSIBLE_REMOVED,
        updateFormsNotifications,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.UPDATE_FORM_CHAT_ASSIGNMENT,
        updateFormChatAssigment,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.USER_SEGMENTATION_UPDATED,
        updateGeneralRedBubbles,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.ADDED_TO_SEGMENTATION_ITEM,
        updateGeneralRedBubbles,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.REMOVED_FROM_SEGMENTATION_ITEM,
        updateGeneralRedBubbles,
      );
    };
  }, [addFormMessage, closeFormChat, addFormRequestMessage]);

  // Tickets
  useEffect(() => {
    const addTicketWrap = (ticket: CreateTicketSocket) => {
      if (!user?.id) return;
      addTicket(user?.id, ticket);
    };

    const updateTopicWrap = (topic: TicketTopicUpdatedSocket) => {
      updateTopic(topic, () => {
        const detailMatch = matchPath(
          ticketSkeletonRoutes.thread.detail(),
          window.location.pathname,
        );

        const ticketItemId = Number(detailMatch?.params?.id);

        if (isTicketOfTopic(ticketItemId, topic.id)) {
          navigate(ticketRoutes.tickets());
          enqueueSnackbar({
            title: t('TICKETS:WARNING_TICKET_TOPIC_UPDATED'),
            variant: 'warning',
          });
        }
      });
    };

    const updateTopicOfTicketWrap = (data: TopicOfTicketUpdatedSocket) => {
      updateTopicOfTicket(data, () => {
        const ticket = getTicket(data?.ticketId);

        const isDetail = !!matchPath(
          ticketRoutes.thread.detail(ticket?.chat?.id),
          window.location.pathname,
        );

        const isResponsible = data?.newTopicResponsibleIds?.some(
          responsibleId => responsibleId === user?.id,
        );

        if (isDetail && !isResponsible) {
          navigate(ticketRoutes.tickets());
          enqueueSnackbar({
            title: t('TICKETS:WARNING_UPDATE_TOPIC_OF_TICKET'),
            variant: 'warning',
          });
        }
      });
    };

    const deleteTopicWrap = (topic: TicketTopicDeletedSocket) => {
      deleteTopic(topic, () => {
        const detailMatch = matchPath(
          ticketSkeletonRoutes.thread.detail(),
          window.location.pathname,
        );

        const ticketItemId = Number(detailMatch?.params?.id);

        if (detailMatch && isTicketOfTopic(ticketItemId, topic.id)) {
          navigate(ticketRoutes.tickets());
          enqueueSnackbar({
            title: t('TICKETS:WARNING_TICKET_TOPIC_DELETED'),
            variant: 'warning',
          });
        }
      });
    };

    const deleteTicketWrap = (ticket: DeleteUnassignedTicketSocket) => {
      if (!user?.id) return;
      deleteTicket(user?.id, ticket, () => {
        const isDetail = !!matchPath(
          ticketRoutes.thread.detail(ticket.chat.id),
          window.location.pathname,
        );

        if (isDetail && !isTicketResponsible(user?.id, ticket)) {
          navigate(ticketRoutes.tickets());
          enqueueSnackbar({
            title: t('TICKETS:WARNING_DELETE_UNASSIGNED_TICKET'),
            variant: 'warning',
          });
        }
      });
    };

    const chatMessageAgentWrap = (ticket: ChatMessageAgentSocket) => {
      const detailMatch = matchPath(
        ticketSkeletonRoutes.thread.detail(),
        window.location.pathname,
      );

      const currentChatId = Number(detailMatch?.params?.id);

      if (!user?.id) return;
      chatMessageAgent(user?.id, ticket, currentChatId);

      const chatId = ticket?.chat?.id;
      const messageUserId = ticket?.chat?.lastMessage?.userId;

      if (!chatId || !messageUserId) return;

      if (
        chatId !== currentChatId &&
        messageUserId !== user?.id &&
        !unread.has(chatId)
      ) {
        unread.add(chatId);
      }
    };

    socket.listenEvent(EVENTS_SOCKETS.CREATE_TICKET, addTicketWrap);
    socket.listenEvent(EVENTS_SOCKETS.CLOSE_TICKET, updateAgentTickets);
    socket.listenEvent(EVENTS_SOCKETS.UPDATE_TICKET_ASSIGNMENT, addMessage);
    socket.listenEvent(EVENTS_SOCKETS.TICKET_TOPIC_UPDATED, updateTopicWrap);
    socket.listenEvent(
      EVENTS_SOCKETS.TOPIC_OF_TICKET_UPDATED,
      updateTopicOfTicketWrap,
    );
    socket.listenEvent(EVENTS_SOCKETS.TICKET_TOPIC_DELETED, deleteTopicWrap);
    socket.listenEvent(
      EVENTS_SOCKETS.DELETE_UNASSIGNED_TICKET,
      deleteTicketWrap,
    );
    socket.listenEvent(EVENTS_SOCKETS.CHAT_MESSAGE_AGENT, chatMessageAgentWrap);

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.CREATE_TICKET, addTicketWrap);
      socket.closeEvent(EVENTS_SOCKETS.CLOSE_TICKET, updateAgentTickets);
      socket.closeEvent(EVENTS_SOCKETS.UPDATE_TICKET_ASSIGNMENT, addMessage);
      socket.closeEvent(EVENTS_SOCKETS.TICKET_TOPIC_UPDATED, updateTopicWrap);
      socket.closeEvent(
        EVENTS_SOCKETS.TOPIC_OF_TICKET_UPDATED,
        updateTopicOfTicketWrap,
      );
      socket.closeEvent(EVENTS_SOCKETS.TICKET_TOPIC_DELETED, deleteTopicWrap);
      socket.closeEvent(
        EVENTS_SOCKETS.DELETE_UNASSIGNED_TICKET,
        deleteTicketWrap,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHAT_MESSAGE_AGENT,
        chatMessageAgentWrap,
      );
    };
  }, [addMessage]);

  // Users
  useEffect(() => {
    const deleteUserWrap = (data: UserDeletedSocket) => {
      deleteUser(() => {
        if (data.id === user?.id) {
          logout?.();
          enqueueSnackbar({
            title: tUsers('WARNING_USER_DELETED'),
            variant: 'warning',
          });
        }
      });
    };

    const deactivateUser = (data: UserDeletedSocket) => {
      if (data.id === user?.id) {
        logout?.();
        enqueueSnackbar({
          title: tUsers('WARNING_USER_DEACTIVATED'),
          variant: 'warning',
        });
      }
    };

    socket.listenEvent(EVENTS_SOCKETS.USER_DELETED, deleteUserWrap);
    socket.listenEvent(EVENTS_SOCKETS.USER_DEACTIVATED, deactivateUser);

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.USER_DELETED, deleteUserWrap);
      socket.closeEvent(EVENTS_SOCKETS.USER_DEACTIVATED, deactivateUser);
    };
  }, []);

  // Chats
  useEffect(() => {
    socket.listenEvent(EVENTS_SOCKETS.CHAT_CREATED, addUnreadChat);
    socket.listenEvent(EVENTS_SOCKETS.CHAT_MESSAGE_USER, addMessage);
    socket.listenEvent(EVENTS_SOCKETS.MESSAGE_EDITED, editMessage);
    socket.listenEvent(EVENTS_SOCKETS.CHAT_MARK_UNREAD, markAsUnread);

    // Chats V2
    socket.listenEvent(EVENTS_SOCKETS.CHATS_V2_NEW_MESSAGE, handleAddMessage);
    socket.listenEvent(EVENTS_SOCKETS.CHATS_V2_READ_CHAT, resetMarkedUnread);
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_UPDATE_MESSAGE,
      handleUpdateMessage,
    );

    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_DELETED_MESSAGE,
      handleDeletedMessage,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_MARKED_RECEIVED,
      handleUpdateReadStatus,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_MARKED_READ,
      handleUpdateReadStatus,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_ADMIN_ADDED,
      handleUpdateGroupInfo,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_ADMIN_REMOVED,
      handleUpdateGroupInfo,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_LEFT_CHAT,
      handleUpdateGroupInfo,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_JOINED_CHAT,
      handleUpdateGroupInfo,
    );
    socket.listenEvent(EVENTS_SOCKETS.CHATS_V2_REACTION_ADDED, handleReaction);
    socket.listenEvent(EVENTS_SOCKETS.CHATS_V2_REACTION_REMOVE, handleReaction);
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_CHANNEL_CHANGED,
      handleUptateGroup,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_PIN_REMOVE,
      handleUpdatePinnedMessages,
    );
    socket.listenEvent(EVENTS_SOCKETS.CHATS_V2_PIN_ADD, handleAddPinnedMessage);
    socket.listenEvent(
      EVENTS_SOCKETS.CHATS_V2_MESSAGE_READ_BY_ALL,
      handleUpdateMessageReadByAll,
    );
    return () => {
      socket.closeEvent(EVENTS_SOCKETS.CHATS_V2_NEW_MESSAGE, handleAddMessage);
      socket.closeEvent(EVENTS_SOCKETS.CHAT_CREATED, addUnreadChat);
      socket.closeEvent(EVENTS_SOCKETS.CHAT_MESSAGE_USER, addMessage);
      socket.closeEvent(EVENTS_SOCKETS.MESSAGE_EDITED, editMessage);
      socket.closeEvent(EVENTS_SOCKETS.CHAT_MARK_UNREAD, markAsUnread);
      socket.closeEvent(EVENTS_SOCKETS.CHATS_V2_READ_CHAT, resetMarkedUnread);
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_UPDATE_MESSAGE,
        handleUpdateMessage,
      );

      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_DELETED_MESSAGE,
        handleDeletedMessage,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_MARKED_RECEIVED,
        handleUpdateReadStatus,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_MARKED_READ,
        handleUpdateReadStatus,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_ADMIN_ADDED,
        handleUpdateGroupInfo,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_ADMIN_REMOVED,
        handleUpdateGroupInfo,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_LEFT_CHAT,
        handleUpdateGroupInfo,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_JOINED_CHAT,
        handleUpdateGroupInfo,
      );
      socket.closeEvent(EVENTS_SOCKETS.CHATS_V2_REACTION_ADDED, handleReaction);
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_REACTION_REMOVE,
        handleReaction,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_CHANNEL_CHANGED,
        handleUptateGroup,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_PIN_REMOVE,
        handleUpdatePinnedMessages,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_PIN_ADD,
        handleAddPinnedMessage,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CHATS_V2_MESSAGE_READ_BY_ALL,
        handleUpdateMessageReadByAll,
      );
    };
  }, [
    addUnreadChat,
    addMessage,
    markAsUnread,
    editMessage,
    resetMarkedUnread,
    handleUpdateGroupInfo,
    handleReaction,
    handleUptateGroup,
    handleAddMessage,
    handleUpdatePinnedMessages,
    handleAddPinnedMessage,
    handleUpdateMessageReadByAll,
  ]);

  useEffect(() => {
    socket.listenEvent(EVENTS_SOCKETS.ARTICLE_CREATED, addArticle);
    socket.listenEvent(EVENTS_SOCKETS.ARTICLE_DELETED, updateArticle);
    socket.listenEvent(
      EVENTS_SOCKETS.UPDATE_ACKNOWLEDGMENT_POINTS,
      updateAcknowledgementsLeftToGive!,
    );

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.ARTICLE_CREATED, addArticle);
      socket.closeEvent(EVENTS_SOCKETS.ARTICLE_DELETED, updateArticle);
      socket.closeEvent(
        EVENTS_SOCKETS.UPDATE_ACKNOWLEDGMENT_POINTS,
        updateAcknowledgementsLeftToGive!,
      );
    };
  }, [addArticle, updateArticle, addOnboarding]);

  // Widgets
  useEffect(() => {
    const deleteWidget = ({ id }: { id: number }) => dropWidget(id);

    socket.listenEvent(EVENTS_SOCKETS.WIDGET_CREATED, pushWidget);
    socket.listenEvent(EVENTS_SOCKETS.WIDGET_DELETED, deleteWidget);
    socket.listenEvent(EVENTS_SOCKETS.WIDGET_UPDATED, updateWidget);
    socket.listenEvent(
      EVENTS_SOCKETS.WIDGET_POSITION_UPDATED,
      updateWidgetList,
    );

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.WIDGET_CREATED, pushWidget);
      socket.closeEvent(EVENTS_SOCKETS.WIDGET_DELETED, deleteWidget);
      socket.closeEvent(EVENTS_SOCKETS.WIDGET_UPDATED, updateWidget);
      socket.closeEvent(
        EVENTS_SOCKETS.WIDGET_POSITION_UPDATED,
        updateWidgetList,
      );
    };
  }, []);

  // Libraries

  useEffect(() => {
    const handleLibraryDeleted = (data: KnowledgeLibraryDeletedSocket) => {
      const libraryMatch = matchPath(
        { path: librariesRoutes.library(), end: false },
        window.location.pathname,
      );

      const currentLibraryId = Number(libraryMatch?.params?.id);
      const isViewingDeletedLibrary =
        libraryMatch && currentLibraryId === data.id;

      if (isViewingDeletedLibrary) {
        navigate(librariesRoutes.base(), { replace: true });
        enqueueSnackbar({
          title: t('libraries:library.not_available'),
          variant: 'error',
        });
      }

      invalidateDeletedLibrary(data.id);
    };

    socket.listenEvent(
      EVENTS_SOCKETS.KNOWLEDGE_LIBRARY_DELETED,
      handleLibraryDeleted,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.KNOWLEDGE_LIBRARY_STATUS_UPDATED,
      updateLibraryStatus,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.KNOWLEDGE_LIBRARY_TITLE_UPDATED,
      updateLibraryStatus,
    );

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.KNOWLEDGE_LIBRARY_DELETED,
        handleLibraryDeleted,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.KNOWLEDGE_LIBRARY_STATUS_UPDATED,
        updateLibraryStatus,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.KNOWLEDGE_LIBRARY_TITLE_UPDATED,
        updateLibraryStatus,
      );
    };
  }, []);

  // Documents
  useEffect(() => {
    const addPendingDocument = (document: Document) => {
      if (isPendingDocument(document)) {
        pushPendingDocumentListData();
      }
      updateDocuments();
    };

    const deletePendingDocument = (data: { id: number }) => {
      dropPendingDocumentListData(data.id);
      updateDocuments();
    };

    const updatePendingDocument = (document: Document) => {
      if (isPendingDocument(document)) {
        setPendingDocumentListData(document.id, document);
      } else {
        dropPendingDocumentListData(document.id);
      }
      updateDocuments();
    };

    socket.listenEvent(EVENTS_SOCKETS.DOCUMENT_CREATED, addPendingDocument);
    socket.listenEvent(EVENTS_SOCKETS.DOCUMENT_UPDATED, updatePendingDocument);
    socket.listenEvent(EVENTS_SOCKETS.DOCUMENT_DELETED, deletePendingDocument);

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.DOCUMENT_CREATED, addPendingDocument);
      socket.closeEvent(EVENTS_SOCKETS.DOCUMENT_UPDATED, updatePendingDocument);
      socket.closeEvent(EVENTS_SOCKETS.DOCUMENT_DELETED, deletePendingDocument);
    };
  }, []);

  // Surveys
  useEffect(() => {
    socket.listenEvent(
      EVENTS_SOCKETS.REMAINING_ATTEMPTS_OF_FORM,
      updateRemainingAttemps,
    );

    socket.listenEvent(EVENTS_SOCKETS.SURVEY_DELETED, deleteSurvey);
    socket.listenEvent(EVENTS_SOCKETS.SURVEY_DISABLED, disabledSurvey);
    socket.listenEvent(EVENTS_SOCKETS.SURVEY_ENABLED, enabledSurvey);

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.REMAINING_ATTEMPTS_OF_FORM,
        updateRemainingAttemps,
      );
      socket.closeEvent(EVENTS_SOCKETS.SURVEY_DELETED, deleteSurvey);
      socket.closeEvent(EVENTS_SOCKETS.SURVEY_DISABLED, disabledSurvey);
      socket.closeEvent(EVENTS_SOCKETS.SURVEY_ENABLED, enabledSurvey);
    };
  }, []);

  // Notifications
  useEffect(() => {
    const clearNotifications = (data: ClearNotificationsSocket) => {
      const { type, id } = data;

      if (type === RedBubbles.ARTICLE) {
        clearNewsNotifications();
      } else if (type === RedBubbles.GROUP) {
        invalidateGroupNotifications();
        invalidateGroupList();
      } else if (type === RedBubbles.CHAT) {
        if (id) updateChatNotifications(id);
      } else if (type === RedBubbles.FORM_CHAT) {
        if (id) updateFormsNotifications();
      } else if (type === RedBubbles.DOCUMENT) {
        if (id) updateDocuments();
      }
    };

    socket.listenEvent(EVENTS_SOCKETS.RED_BUBBLE_CLEARED, clearNotifications);

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.RED_BUBBLE_CLEARED, clearNotifications);
    };
  }, []);

  // Service Management — Bubbaloo
  useEffect(() => {
    const onBubblesUpdated = ({ bubbleCode }: { bubbleCode: string }) => {
      queryClient.invalidateQueries(
        serviceManagementKeys.bubbaloo.count(bubbleCode as BubbleCode),
      );
    };

    socket.listenEvent(EVENTS_SOCKETS.BUBBLES_UPDATED, onBubblesUpdated);

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.BUBBLES_UPDATED, onBubblesUpdated);
    };
  }, [queryClient]);

  // Calls
  useEffect(() => {
    const addCallChatEvent = (updatedMessage: Message) => {
      queryClient.invalidateQueries(
        chatKeys.messages.list(updatedMessage?.chatId!),
      );
    };

    const updateParticipantList = (data: CallRefreshParticipants) => {
      invalidateParticipantList();
      invalidateVideoCall(data.id);
    };

    const updateCallActiveStatus = (data: CallEvent) => {
      invalidateActiveVideoCall(data.metadata.channelId);
    };

    socket.listenEvent(
      EVENTS_SOCKETS.CALL_CHAT_MESSAGE_CREATED,
      addCallChatEvent,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.CALL_REFRESH_PARTICIPANTS,
      updateParticipantList,
    );
    socket.listenEvent(EVENTS_SOCKETS.CALL_INITIALIZED, updateCallActiveStatus);
    socket.listenEvent(EVENTS_SOCKETS.CALL_STARTED, updateCallActiveStatus);
    socket.listenEvent(EVENTS_SOCKETS.CALL_ENDED, updateCallActiveStatus);

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.CALL_CHAT_MESSAGE_CREATED,
        addCallChatEvent,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CALL_REFRESH_PARTICIPANTS,
        updateParticipantList,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.CALL_INITIALIZED,
        updateCallActiveStatus,
      );
      socket.closeEvent(EVENTS_SOCKETS.CALL_STARTED, updateCallActiveStatus);
      socket.closeEvent(EVENTS_SOCKETS.CALL_ENDED, updateCallActiveStatus);
    };
  }, []);

  // Time Tracking - Categorized hours approval flow
  useEffect(() => {
    socket.listenEvent(
      EVENTS_SOCKETS.CATEGORIZED_HOUR_STATUS_UPDATED,
      handleCategorizedHourStatusUpdated,
    );

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.CATEGORIZED_HOUR_STATUS_UPDATED,
        handleCategorizedHourStatusUpdated,
      );
    };
  }, []);

  return (
    <SocketContext.Provider value={socket}>{children}</SocketContext.Provider>
  );
};

export const useSocket = () => useContext(SocketContext);

export const SocketConsumer = SocketContext.Consumer;

export default SocketContext;
