package com.atlassian.stash.internal.pull;

import com.atlassian.bitbucket.comment.CommentAction;
import com.atlassian.bitbucket.comment.commit.AddCommitFileCommentRequest;
import com.atlassian.bitbucket.comment.commit.AddCommitLineCommentRequest;
import com.atlassian.bitbucket.pull.*;
import com.atlassian.stash.experimental.pull.ExperimentalPullRequestService;
import com.atlassian.stash.internal.comment.InternalComment;
import com.atlassian.stash.internal.pull.rescope.SimplePullRequestRescope;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Map;

/**
 * Non-public extensions to the {@link PullRequestService} which are not intended to be available to plugins.
 *
 * @since 1.3
 */
public interface InternalPullRequestService extends ExperimentalPullRequestService {

    /**
     * Add comment related activity to the pull request
     * @param pullRequest the pull request to which the activity should be added
     * @param comment the comment the activity relates to
     * @param commentAction the action performed
     * @since 4.7
     */
    void addCommentActivity(@Nonnull InternalPullRequest pullRequest, @Nonnull InternalComment comment,
                            @Nonnull CommentAction commentAction);

    /**
     * {@link PullRequestDiffCommentAnchor Anchor} the provided line {@link InternalComment comment} to the specified
     * pull request.
     *
     * @param repositoryId the id of the pull request scope repository
     * @param pullRequestId the scoped id of the pull request in the repository
     * @param comment the comment which should be anchored to the pull request
     * @param request a request describing the comment to create
     * @return the newly created anchor
     * @since 4.7
     */
    @Nonnull
    InternalPullRequestDiffCommentAnchor addLineAnchor(int repositoryId, long pullRequestId,
                                                       @Nonnull InternalComment comment,
                                                       @Nonnull AddCommitLineCommentRequest request);

    /**
     * {@link PullRequestDiffCommentAnchor Anchor} the provided file {@link InternalComment comment} to the specified
     * pull request.
     *
     * @param repositoryId the id of the pull request scope repository
     * @param pullRequestId the scoped id of the pull request in the repository
     * @param comment the comment which should be anchored to the pull request
     * @param request a request describing the comment to create
     * @return the newly created anchor
     * @since 4.7
     */
    @Nonnull
    InternalPullRequestDiffCommentAnchor addFileAnchor(int repositoryId, long pullRequestId,
                                                       @Nonnull InternalComment comment,
                                                       @Nonnull AddCommitFileCommentRequest request);

    /**
     * Puts {@code pullRequest} in a {@code MERGED} state, without performing a merge. This method should only be called
     * for pull requests that should be closed because they have been merged manually (not through Stash).
     *
     * @param globalId the global id of the pull request
     * @param version the version of the pull request
     * @return the updated pull request (updated version)
     * @throws NoSuchPullRequestException when the pull request cannot be found.
     * @throws PullRequestOutOfDateException when the current version of the pull request does
     *         not match {@code version}
     * @deprecated in 4.5, to be removed when the old rescoping logic is culled
     */
    @Deprecated
    @Nonnull
    PullRequest closeMerged(long globalId, int version);

    /**
     * Puts {@code pullRequest} in the {@code DECLINED} state.
     *
     * @param globalId the global id of the pull request
     * @param version  the version of the pull request
     * @return the updated pull request (updated version)
     * @throws NoSuchPullRequestException when the pull request cannot be found.
     * @throws PullRequestOutOfDateException when the current version of the pull request does
     *         not match {@code version}
     * @deprecated in 4.5, to be removed when the old rescoping logic is culled
     */
    @Deprecated
    @Nonnull
    PullRequest decline(long globalId, int version);

    /**
     * Ensures the specified {@link PullRequest} is up-to-date by resolving its effective diff.
     * <p>
     * This is a <i>hack</i> which exploits the fact that resolving a pull request's effective diff implicitly fetches
     * all commits into the target repository. It's included in 2.4 to work around the following race condition:
     * <ul>
     *     <li>A cross-repository pull request with no comments is rescoped on the from side, adding new commits</li>
     *     <li>Notifications with links to added commits are sent to participants</li>
     *     <li>Because there are no comments the lazy merge is not done, so the commits are not fetched into the target
     *     repository (comment drift optimises out resolving the effective diff)</li>
     *     <li>Clicking a commit link in the notification e-mail goes straight to the target repository and results
     *     in a 404</li>
     * </ul>
     * This hack will be removed when viewing a commit in the context of a pull request is supported.
     *
     * @param pullRequest the pull request to resolve the effective diff for
     * @since 2.4
     */
    void ensureUpToDate(@Nonnull PullRequest pullRequest);

    /**
     * force declines a pull request ignoring the usual pre conditions such as if the pull request was already merged.
     *
     * @param pullRequestDeclineRequest the pull request to be declined
     * @return the updated pull request
     * @since 4.8
     */
    void forceDecline(@Nonnull PullRequestDeclineRequest pullRequestDeclineRequest);

    /**
     * Force reopen a merged or declined pull request ignoring the usual pre conditions such as if the pull request
     * was already merged.
     *
     * @param repositoryId the repository ID
     * @param pullRequestId the pull request ID
     * @return the updated pull request
     * @throws DuplicatePullRequestException if a pull request with the same source and target refs already exists
     * @since 4.8
     */
    @Nonnull
    PullRequest forceReopen(int repositoryId, long pullRequestId);

    /**
     * Returns the number of pull requests in this installation by status.
     *
     * @return the number of pull requests in this installation by status
     * @since 3.7
     */
    Map<PullRequestState, Long> getCountsByState();

    /**
     * Rescopes the pull request
     *
     * @param globalId the global ID of the pull request
     * @param version the version of the pull request
     * @param rescopes describes how the pull request needs to be updated
     * @return the updated pull request
     */
    @Nonnull
    PullRequest rescope(long globalId, int version, @Nonnull List<SimplePullRequestRescope> rescopes);

    /**
     * Rescopes a pull request.
     *
     * @param globalId the global id of the pull request
     * @param version the version of the pull request
     * @param fromHash the new fromHash.
     * @param toHash the new toHash.
     * @return the updated pull request.
     * @throws NoSuchPullRequestException when the pull request cannot be found.
     * @throws PullRequestOutOfDateException when the current version of the pull request does
     *         not match {@code version}
     * @deprecated in 4.5, to be removed when the old rescoping logic is culled
     */
    @Deprecated
    @Nonnull
    InternalPullRequest updateScope(long globalId, int version, @Nonnull String fromHash, @Nonnull String toHash);
}
