import { useEffect } from 'react';
import { useMutation } from 'react-query';

import { EVENTS_SOCKETS } from 'src/constants/sockets';
import { useAuth } from 'src/contexts/JWTContext';
import { useSocket } from 'src/contexts/SocketContext';
import { joinEventSocketRoom } from 'src/services/events';
import { Comment } from 'src/types/comments';
import {
  type EventPostHlsStreamSocket,
  type EventPostStreamSocket,
} from 'src/types/posts';
import { Reaction } from 'src/types/reaction';
import { addReaction, removeReaction } from 'src/utils/reactions';

import {
  dropEventPostChildComment,
  dropEventPostComment,
  invalidateEventPostsWithLivestream,
  invalidateEventPosts,
  unshiftEventPostChildComment,
  unshiftEventPostComment,
  updateEventPostChildComment,
  updateEventPostChildCommentReaction,
  updateEventPostComment,
  updateEventPostCommentReaction,
  updateEventPostReaction,
} from '../queries';

export const usePostsSocket = (refetchPosts: () => void, eventId: number) => {
  const socket = useSocket();
  useEffect(() => {
    const handlePostUpdate = () => {
      refetchPosts();
      invalidateEventPostsWithLivestream(eventId);
    };

    socket.listenEvent(EVENTS_SOCKETS.EVENT_POST_CREATED, handlePostUpdate);
    socket.listenEvent(EVENTS_SOCKETS.EVENT_POST_EDITED, handlePostUpdate);
    socket.listenEvent(EVENTS_SOCKETS.EVENT_POST_DELETED, handlePostUpdate);

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.EVENT_POST_CREATED, handlePostUpdate);
      socket.closeEvent(EVENTS_SOCKETS.EVENT_POST_EDITED, handlePostUpdate);
      socket.closeEvent(EVENTS_SOCKETS.EVENT_POST_DELETED, handlePostUpdate);
    };
  }, [socket, refetchPosts, eventId]);
};

export const useEventRoom = (eventId: number) => {
  const socket = useSocket();
  const { user } = useAuth();

  const joinRoomMutation = useMutation(() => joinEventSocketRoom(eventId), {
    onSuccess: response => {
      socket.emitEvent(EVENTS_SOCKETS.JOIN_ROOM, {
        userId: user?.id.toString(),
        ticketId: response.data.ticketId,
      });
    },
  });

  useEffect(() => {
    joinRoomMutation.mutate();
  }, []);
};

export const useEventPostCommentsSocket = (eventId: number) => {
  const socket = useSocket();
  const { user } = useAuth();

  useEffect(() => {
    const addNewComment = (
      comment: Comment & {
        postId: number;
        eventId: number;
        parentId: number | null;
      },
    ) => {
      if (comment.eventId !== eventId) return;

      invalidateEventPostsWithLivestream(eventId);

      if (comment.parentId !== null) {
        unshiftEventPostChildComment(
          comment.postId,
          eventId,
          comment.parentId,
          comment,
        );
      } else {
        unshiftEventPostComment(comment.postId, eventId, comment);
      }
    };

    const updateComment = (
      comment: Comment & {
        postId: number;
        eventId: number;
        parentId: number | null;
      },
    ) => {
      if (comment.eventId !== eventId) return;

      if (comment.parentId !== null) {
        updateEventPostChildComment(
          comment.postId,
          eventId,
          comment.parentId,
          comment,
        );
      } else {
        updateEventPostComment(comment.postId, eventId, comment);
      }
    };

    const removeComment = (data: {
      postId: number;
      eventId: number;
      parentId: number | null;
      commentId: number;
    }) => {
      if (data.eventId !== eventId) return;

      if (data.parentId !== null) {
        dropEventPostChildComment(
          data.postId,
          eventId,
          data.parentId,
          data.commentId,
        );
      } else {
        dropEventPostComment(data.postId, eventId, data.commentId);
      }
    };

    const addPostReaction = (data: {
      reaction: Reaction;
      eventId: number;
      postId: number;
      userId: number;
    }) => {
      if (data.eventId !== eventId) return;

      updateEventPostReaction(eventId, data.postId, reactions =>
        addReaction(
          reactions,
          data.reaction.emoji,
          data.reaction.unified,
          data.userId === user?.id,
        ),
      );
    };

    const removePostReaction = (data: {
      emoji: string;
      eventId: number;
      postId: number;
      reactionId: number;
      userId: number;
    }) => {
      if (data.eventId !== eventId) return;

      updateEventPostReaction(eventId, data.postId, reactions =>
        removeReaction(reactions, data.emoji, data.userId === user?.id),
      );
    };

    const addCommentReaction = (data: {
      reaction: Reaction;
      eventId: number;
      postId: number;
      parentId: number | null;
      commentId: number;
      userId: number;
    }) => {
      if (data.eventId !== eventId) return;

      const updater = (reactions: any) =>
        addReaction(
          reactions,
          data.reaction.emoji,
          data.reaction.unified,
          data.userId === user?.id,
        );

      if (data.parentId !== null) {
        updateEventPostChildCommentReaction(
          data.postId,
          eventId,
          data.parentId,
          data.commentId,
          updater,
        );
      } else {
        updateEventPostCommentReaction(
          data.postId,
          eventId,
          data.commentId,
          updater,
        );
      }
    };

    const removeCommentReaction = (data: {
      commentId: number;
      emoji: string;
      eventId: number;
      parentId: number | null;
      postId: number;
      reactionId: number;
      userId: number;
    }) => {
      if (data.eventId !== eventId) return;

      const updater = (reactions: Reaction[] | undefined) =>
        removeReaction(reactions, data.emoji, data.userId === user?.id);

      if (data.parentId !== null) {
        updateEventPostChildCommentReaction(
          data.postId,
          eventId,
          data.parentId,
          data.commentId,
          updater,
        );
      } else {
        updateEventPostCommentReaction(
          data.postId,
          eventId,
          data.commentId,
          updater,
        );
      }
    };

    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_COMMENT_CREATED,
      addNewComment,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_COMMENT_UPDATED,
      updateComment,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_COMMENT_DELETED,
      removeComment,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_REACTION_CREATED,
      addPostReaction,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_REACTION_DELETED,
      removePostReaction,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_COMMENT_REACTION_CREATED,
      addCommentReaction,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_COMMENT_REACTION_DELETED,
      removeCommentReaction,
    );

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_COMMENT_CREATED,
        addNewComment,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_COMMENT_UPDATED,
        updateComment,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_COMMENT_DELETED,
        removeComment,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_REACTION_CREATED,
        addPostReaction,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_REACTION_DELETED,
        removePostReaction,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_COMMENT_REACTION_CREATED,
        addCommentReaction,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_COMMENT_REACTION_DELETED,
        removeCommentReaction,
      );
    };
  }, [socket, eventId, user?.id]);
};

export const useEventLivestreamSocket = (eventId: number) => {
  const socket = useSocket();

  useEffect(() => {
    const handleStreamUpdate = (data: EventPostStreamSocket) => {
      if (data.eventId !== eventId) return;
      invalidateEventPosts(eventId);
      invalidateEventPostsWithLivestream(eventId);
    };

    const handleHlsUpdate = (data: EventPostHlsStreamSocket) => {
      if (data.eventId !== eventId) return;
      invalidateEventPosts(eventId);
      invalidateEventPostsWithLivestream(eventId);
    };

    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_LIVESTREAM_ENDED,
      handleStreamUpdate,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_LIVESTREAM_RECORDED,
      handleStreamUpdate,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_LIVESTREAM_CAPTIONS_ENABLED,
      handleStreamUpdate,
    );
    socket.listenEvent(
      EVENTS_SOCKETS.EVENT_POST_LIVESTREAM_HLS_STARTED,
      handleHlsUpdate,
    );

    return () => {
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_LIVESTREAM_ENDED,
        handleStreamUpdate,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_LIVESTREAM_RECORDED,
        handleStreamUpdate,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_LIVESTREAM_CAPTIONS_ENABLED,
        handleStreamUpdate,
      );
      socket.closeEvent(
        EVENTS_SOCKETS.EVENT_POST_LIVESTREAM_HLS_STARTED,
        handleHlsUpdate,
      );
    };
  }, [socket, eventId]);
};
