package com.atlassian.stash.internal.comment.like.notification;

import com.atlassian.stash.internal.comment.like.InternalCommentLikeService;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Map;

/**
 * Filter {@link CommentLikeData comment like data}.
 */
class CommentLikeFilter {

    private final InternalCommentLikeService commentLikeService;

    public CommentLikeFilter(@Nonnull InternalCommentLikeService commentLikeService) {
        this.commentLikeService = commentLikeService;
    }

    /**
     * Filters out the removed likes from an existing collection of like data.
     *
     * @param likeData like data to examine
     * @return the provided {@code likeData} without any removed likes
     */
    public Multimap<ApplicationUser, CommentLikeData> filterRemovedLikes(Multimap<ApplicationUser, CommentLikeData> likeData) {
        // generate the 'comment id' -> 'like user id' associations
        Map<Long, Collection<Integer>> associations = asCommentAssociations(likeData);

        // use them to determine which likes still exist
        Map<Long, Collection<Integer>> validAssociations = commentLikeService.retainExisting(associations);

        // and filter out the original like data if needed
        return !validAssociations.equals(associations) ? filterNonExistent(likeData, validAssociations) : likeData;
    }

    private Map<Long, Collection<Integer>> asCommentAssociations(Multimap<ApplicationUser, CommentLikeData> userToLikeData) {
        Multimap<Long, Integer> commentsToUsers = HashMultimap.create();
        for (Map.Entry<ApplicationUser, CommentLikeData> entry : userToLikeData.entries()) {
            CommentLikeData data = entry.getValue();
            for (ApplicationUser liker : data.getLikers()) {
                commentsToUsers.put(data.getComment().getId(), liker.getId());
            }
        }
        return commentsToUsers.asMap();
    }

    private Multimap<ApplicationUser, CommentLikeData> filterNonExistent(Multimap<ApplicationUser, CommentLikeData> allLikeData,
                                                                   Map<Long, Collection<Integer>> existingLikes) {
        Multimap<ApplicationUser, CommentLikeData> result = ArrayListMultimap.create();
        for (Map.Entry<ApplicationUser, CommentLikeData> entry : allLikeData.entries()) {
            ApplicationUser recipient = entry.getKey();
            CommentLikeData likeData = entry.getValue();
            CommentLikeData filteredLikeData = filterLikeData(existingLikes, likeData);
            if (filteredLikeData != null) {
                result.put(recipient, filteredLikeData);
            }
        }
        return result;
    }

    @Nullable
    private CommentLikeData filterLikeData(Map<Long, Collection<Integer>> existingLikes, CommentLikeData likeData) {
        Collection<Integer> existingLikesForComment = existingLikes.get(likeData.getComment().getId());
        if (existingLikesForComment == null || existingLikesForComment.isEmpty()) {
            return null;
        }

        CommentLikeData filtered = new CommentLikeData(likeData.getComment());
        for (ApplicationUser originalLiker : likeData.getLikers()) {
            if (existingLikesForComment.contains(originalLiker.getId())) {
                filtered.addLike(originalLiker, likeData.getDate());
            }
        }

        if (filtered.getLikers().isEmpty()) {
            return null;
        }

        return filtered;
    }

}
