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

import { useCallStateHooks } from '@stream-io/video-react-sdk';

import { EVENTS_SOCKETS } from 'src/constants/sockets';
import { useAuth } from 'src/contexts/JWTContext';
import { useSocket } from 'src/contexts/SocketContext';
import useGeneralError from 'src/hooks/useGeneralError';
import {
  dropEventPostComment,
  unshiftEventPostComment,
  updateEventPostComment,
  updateEventPostReaction,
} from 'src/pages/dashboard/events/queries';
import {
  addGroupFeedDetailDataReaction,
  dropGroupComment,
  removeGroupFeedDetailDataReaction,
  unshiftGroupComment,
  updateGroupComment,
} from 'src/pages/dashboard/groups/queries';
import { type Comment } from 'src/types/comments';
import { type PostReactionSocket } from 'src/types/posts';
import { type Reaction } from 'src/types/reaction';
import { StreamDestination } from 'src/types/stream';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { addReaction, removeReaction } from 'src/utils/reactions';

import {
  addFeedDetailDataReaction,
  removeFeedDetailDataReaction,
} from '../../feed/queries';

import { useStreamDestination } from './useStreamDestination';

type Params = {
  postId: number;
};

export const useGetStatistics = (props: Params) => {
  const { postId } = props;
  const socket = useSocket();
  const showGeneralError = useGeneralError();
  const { t } = useLokaliseTranslation(['livestream', 'post']);
  const { user } = useAuth();
  const { useParticipantCount, useCallCustomData } = useCallStateHooks();
  const participantCount = useParticipantCount();
  const { isRtmpConnection } = useCallCustomData();

  const {
    destination,
    details: destinationDetails,
    actions: destinationActions,
    keys: destinationKeys,
  } = useStreamDestination();

  useEffect(() => {
    // Feed
    const addPostReaction = (reaction: PostReactionSocket) => {
      const { emoji, unified, userId, postId: postIdReaction } = reaction;

      if (postIdReaction === postId) {
        addFeedDetailDataReaction(
          postIdReaction,
          emoji,
          unified,
          userId === user?.id,
        );
      }
    };
    const removePostReaction = (reaction: PostReactionSocket) => {
      const { emoji, userId, postId: postIdReaction } = reaction;
      if (postIdReaction === postId) {
        removeFeedDetailDataReaction(
          postIdReaction,
          emoji,
          userId === user?.id,
        );
      }
    };

    // Groups
    const addNewGroupPostComment = ({
      comment,
      groupId,
      postId: updatedPostId,
    }: {
      comment: Comment;
      groupId: number;
      postId: number;
    }) => {
      !comment.parentId &&
        unshiftGroupComment(updatedPostId, groupId.toString(), comment);
    };
    const updateGroupPostComment = ({
      comment,
      groupId,
      postId: updatedPostId,
    }: {
      comment: Comment;
      groupId: number;
      postId: number;
    }) => {
      !comment.parentId &&
        updateGroupComment(updatedPostId, groupId.toString(), comment);
    };
    const addNewGroupPostReaction = ({
      reaction,
      groupId,
      postId: updatedPostId,
    }: {
      reaction: Reaction;
      groupId: number;
      postId: number;
    }) => {
      addGroupFeedDetailDataReaction(
        updatedPostId,
        groupId.toString(),
        reaction.emoji,
        reaction.unified,
        reaction.isLoggedUserReaction,
      );
    };
    const removeGroupPostComment = ({
      commentId,
      parentId,
      groupId,
      postId: updatedPostId,
    }: {
      commentId: number;
      parentId: number | null;
      groupId: number;
      postId: number;
    }) => {
      !parentId &&
        dropGroupComment(updatedPostId, groupId.toString(), commentId);
    };
    const removeGroupPostReaction = ({
      emoji,
      groupId,
      postId: updatedPostId,
      userId,
    }: {
      emoji: string;
      groupId: number;
      postId: number;
      userId: number;
    }) => {
      removeGroupFeedDetailDataReaction(
        updatedPostId,
        groupId.toString(),
        emoji,
        userId === user?.id,
      );
    };

    // Events
    const eventId = destinationDetails?.id;

    const addNewEventPostComment = (
      comment: Comment & {
        postId: number;
        eventId: number;
        parentId: number | null;
      },
    ) => {
      if (comment.eventId !== eventId || comment.parentId !== null) return;
      unshiftEventPostComment(comment.postId, eventId, comment);
    };
    const updateEventPostCommentHandler = (
      comment: Comment & {
        postId: number;
        eventId: number;
        parentId: number | null;
      },
    ) => {
      if (comment.eventId !== eventId || comment.parentId !== null) return;
      updateEventPostComment(comment.postId, eventId, comment);
    };
    const removeEventPostComment = (data: {
      postId: number;
      eventId: number;
      parentId: number | null;
      commentId: number;
    }) => {
      if (data.eventId !== eventId || data.parentId !== null) return;
      dropEventPostComment(data.postId, eventId, data.commentId);
    };
    const addNewEventPostReaction = (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 removeEventPostReaction = (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),
      );
    };

    if (destination === StreamDestination.FEED) {
      socket.listenEvent(EVENTS_SOCKETS.NEW_POST_REACTION, addPostReaction);
      socket.listenEvent(
        EVENTS_SOCKETS.REMOVE_POST_REACTION,
        removePostReaction,
      );
    }

    if (destination === StreamDestination.GROUPS) {
      socket.listenEvent(
        EVENTS_SOCKETS.GROUP_POST_COMMENT_CREATED,
        addNewGroupPostComment,
      );
      socket.listenEvent(
        EVENTS_SOCKETS.GROUP_POST_COMMENT_EDITED,
        updateGroupPostComment,
      );
      socket.listenEvent(
        EVENTS_SOCKETS.GROUP_POST_REACTION_CREATED,
        addNewGroupPostReaction,
      );
      socket.listenEvent(
        EVENTS_SOCKETS.GROUP_POST_COMMENT_DELETED,
        removeGroupPostComment,
      );
      socket.listenEvent(
        EVENTS_SOCKETS.GROUP_POST_REACTION_DELETED,
        removeGroupPostReaction,
      );
    }

    if (destination === StreamDestination.EVENTS) {
      socket.listenEvent(
        EVENTS_SOCKETS.EVENT_POST_COMMENT_CREATED,
        addNewEventPostComment,
      );
      socket.listenEvent(
        EVENTS_SOCKETS.EVENT_POST_COMMENT_UPDATED,
        updateEventPostCommentHandler,
      );
      socket.listenEvent(
        EVENTS_SOCKETS.EVENT_POST_COMMENT_DELETED,
        removeEventPostComment,
      );
      socket.listenEvent(
        EVENTS_SOCKETS.EVENT_POST_REACTION_CREATED,
        addNewEventPostReaction,
      );
      socket.listenEvent(
        EVENTS_SOCKETS.EVENT_POST_REACTION_DELETED,
        removeEventPostReaction,
      );
    }

    return () => {
      if (destination === StreamDestination.FEED) {
        socket.closeEvent(EVENTS_SOCKETS.NEW_POST_REACTION, addPostReaction);
        socket.closeEvent(
          EVENTS_SOCKETS.REMOVE_POST_REACTION,
          removePostReaction,
        );
      }

      if (destination === StreamDestination.GROUPS) {
        socket.closeEvent(
          EVENTS_SOCKETS.GROUP_POST_COMMENT_CREATED,
          addNewGroupPostComment,
        );

        socket.closeEvent(
          EVENTS_SOCKETS.GROUP_POST_COMMENT_EDITED,
          updateGroupPostComment,
        );

        socket.closeEvent(
          EVENTS_SOCKETS.GROUP_POST_REACTION_CREATED,
          addNewGroupPostReaction,
        );

        socket.closeEvent(
          EVENTS_SOCKETS.GROUP_POST_COMMENT_DELETED,
          removeGroupPostComment,
        );

        socket.closeEvent(
          EVENTS_SOCKETS.GROUP_POST_REACTION_DELETED,
          removeGroupPostReaction,
        );
      }

      if (destination === StreamDestination.EVENTS) {
        socket.closeEvent(
          EVENTS_SOCKETS.EVENT_POST_COMMENT_CREATED,
          addNewEventPostComment,
        );
        socket.closeEvent(
          EVENTS_SOCKETS.EVENT_POST_COMMENT_UPDATED,
          updateEventPostCommentHandler,
        );
        socket.closeEvent(
          EVENTS_SOCKETS.EVENT_POST_COMMENT_DELETED,
          removeEventPostComment,
        );
        socket.closeEvent(
          EVENTS_SOCKETS.EVENT_POST_REACTION_CREATED,
          addNewEventPostReaction,
        );
        socket.closeEvent(
          EVENTS_SOCKETS.EVENT_POST_REACTION_DELETED,
          removeEventPostReaction,
        );
      }
    };
  }, [socket, user?.id, destination, destinationDetails?.id, postId]);

  const { data: post } = useQuery(
    destinationKeys.postDetail(postId),
    () => destinationActions.getPostById(postId),
    {
      select: response => response.data,
      onError: err => {
        showGeneralError(err, t('post:error_loading_post_detail'));
      },
    },
  );

  const reactionsCount =
    post?.reactions.reduce((acc, reaction) => acc + reaction.count, 0) || 0;

  const realViewerCount = isRtmpConnection
    ? participantCount - 2
    : participantCount - 1;

  return {
    reactionsCount,
    commentCount: post?.commentCount || 0,
    participantCount: realViewerCount >= 0 ? realViewerCount : 0,
  };
};
