package com.atlassian.stash.internal.notification.mention;

import com.atlassian.bitbucket.comment.Comment;
import com.atlassian.bitbucket.notification.Notification;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.permission.PermissionService;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.user.UserService;
import com.atlassian.bitbucket.util.Chainable;
import com.atlassian.fugue.Pair;
import com.atlassian.stash.internal.notification.handlers.AbstractNotificationHandler;

import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;

import static com.atlassian.bitbucket.util.MoreCollectors.toImmutableList;
import static com.atlassian.bitbucket.util.MoreStreams.streamIterable;

/**
 * A wrapper around {@link MentionScanner} that knows about users and permissions.
 */
public class MentionHelper {

    public static final String MENTIONS_ENABLED = "mentions.enabled";

    private final ApplicationPropertiesService propertiesService;
    private final MentionScanner mentionScanner;
    private final UserService userService;
    private final Predicate<ApplicationUser> isLicensedUser;

    public MentionHelper(ApplicationPropertiesService propertiesService, MentionScanner mentionScanner,
                         UserService userService, PermissionService permissionService) {
        this(propertiesService, mentionScanner, userService, user -> {
            return permissionService.hasGlobalPermission(user, Permission.LICENSED_USER);
        });
    }

    public MentionHelper(ApplicationPropertiesService propertiesService, MentionScanner mentionScanner,
                         UserService userService, Predicate<ApplicationUser> isLicensedUser) {
        this.propertiesService = propertiesService;
        this.mentionScanner = mentionScanner;
        this.userService = userService;
        this.isLicensedUser = isLicensedUser;
    }

    public Set<ApplicationUser> findUsers(Notification notification, CharSequence text) {
        return findUsers(notification, mentionScanner.getMentionedUsers(text));
    }

    public Set<ApplicationUser> findUsers(Notification notification, Set<String> mentionedUsers) {
        return Chainable.chain(userService.getUsersByName(mentionedUsers))
                .filter(mentionedUser -> !Objects.equals(mentionedUser, notification.getUser()))
                .filter(isLicensedUser)
                .toSet();
    }

    public Set<String> getMentionedUsersDifference(CharSequence previousText, CharSequence text) {
        return mentionScanner.getMentionedUsersDifference(previousText, text);
    }

    public Set<String> getMentionedUsers(CharSequence text) {
        return mentionScanner.getMentionedUsers(text);
    }

    /**
     * Return a {@code Pair} of {@code Iterable} {@code ApplicationUser}'s, where the left hand side contains a filtered
     * set of recipients, and the right contains the mentioned users.
     */
    public Pair<Iterable<ApplicationUser>, Set<ApplicationUser>> getFilteredRecipientsAndMentions(Notification notification, Comment comment, Iterable<ApplicationUser> recipients) {
        if (!getProperty(MENTIONS_ENABLED, false)) {
            return Pair.pair(recipients, Collections.<ApplicationUser>emptySet());
        }

        Set<ApplicationUser> mentionedUsers = findUsers(notification, getMentionedUsers(comment));
        return Pair.pair(streamIterable(recipients)
                .filter(recipient -> !mentionedUsers.contains(recipient))
                .collect(toImmutableList()), mentionedUsers);
    }

    protected Set<String> getMentionedUsers(Comment comment) {
        return mentionScanner.getMentionedUsers(comment.getText());
    }

    private boolean getProperty(String name, boolean defaultValue) {
        return propertiesService.getPluginProperty(AbstractNotificationHandler.PLUGIN_PREFIX + name, defaultValue);
    }
}
