package com.atlassian.stash.internal.notification.pull.handlers;

import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.commit.CommitService;
import com.atlassian.bitbucket.commit.CommitsBetweenRequest;
import com.atlassian.bitbucket.throttle.ResourceBusyException;
import com.atlassian.bitbucket.pull.PullRequest;
import com.atlassian.bitbucket.user.EscalatedSecurityContext;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.user.SecurityService;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageUtils;
import com.atlassian.bitbucket.util.UncheckedOperation;

import static com.atlassian.bitbucket.util.PageUtils.createEmptyPage;
import static com.atlassian.bitbucket.util.PageUtils.newRequest;

public class PullRequestNotificationHelper {

    public static final String COMMITS_KEY = "commits";
    public static final Page<Commit> THROTTLED = createEmptyPage(newRequest(0, 0));

    private final CommitService commitService;
    private final EscalatedSecurityContext withRepoRead;

    public PullRequestNotificationHelper(CommitService commitService, SecurityService securityService) {
        this.commitService = commitService;
        this.withRepoRead = securityService.withPermission(Permission.REPO_READ, "PR email plugin");
    }

    public Page<Commit> getNewCommits(PullRequest pullRequest, String previousFromHash, int limit) {
        return withRepoRead.call(() -> {
            //In addition to excluding the to ref's current commit (done automatically by the builder) also
            //exclude the from ref's previous commit. Any commits reachable from the previous commit are
            //not "added", in the context of a rescope. Only commits that are currently reachable (included by
            //the from ref's current commit) that were _not_ previously reachable are "added"
            CommitsBetweenRequest request = new CommitsBetweenRequest.Builder(pullRequest)
                    .exclude(previousFromHash)
                    .build();

            return fetchCommits(request, limit);
        });
    }

    public Page<Commit> getCommits(PullRequest pullRequest, int limit) {
        return withRepoRead.call(() -> fetchCommits(new CommitsBetweenRequest.Builder(pullRequest).build(), limit));
    }

    private Page<Commit> fetchCommits(CommitsBetweenRequest request, int limit) {
        try {
            //The CommitService internally over-fetches the PageRequest by one, to allow setting Page.getIsLastPage(),
            //so there is no need to over-fetch and reduce here
            return commitService.getCommitsBetween(request, newRequest(0, limit));
        } catch (ResourceBusyException e) {
            return THROTTLED;
        }
    }
}
