package com.atlassian.stash.internal.pull;

import com.atlassian.bitbucket.comment.Comment;
import com.atlassian.bitbucket.pull.PullRequestState;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;
import com.atlassian.stash.internal.Dao;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.function.Predicate;

/**
 * DAO for dealing with {@link InternalPullRequest pull requests}
 *
 * @since 1.3
 */
public interface PullRequestDao extends Dao<Long, InternalPullRequest> {

    /**
     * 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> countAllByStatus();

    /**
     * Counts the number of pull requests matching the supplied criteria.
     *
     * @param criteria the criteria to use for matching
     * @return the matching count
     */
    long countMatching(@Nonnull PullRequestSearchCriteria criteria);

    /**
     * Declines any {@link PullRequestState#OPEN open} pull requests {@link InternalPullRequest#getFromRef() from} the
     * specified repository.
     *
     * @param repositoryId the ID of the repository from which pull requests should be declined
     * @return how many pull requests were declined
     */
    int declineByFromRepository(int repositoryId);

    /**
     * Deletes all pull requests {@link InternalPullRequest#getToRef() to} the specified repository. Pull requests
     * {@link InternalPullRequest#getFromRef() from} the specified repository are not deleted <i>unless</i> they are
     * also {@link InternalPullRequest#getToRef() to} it.
     *
     * @param repositoryId the ID of the repository to which pull requests should be deleted
     * @return how many pull requests were deleted
     */
    int deleteByToRepository(int repositoryId);

    /**
     * Finds the {@link InternalPullRequest} that contains the {@link Comment comment} with the given ID.
     *
     * @param commentId the ID of the comment the pull request should contain
     * @return the pull request that contains the comment, or {@code null} if no pull request was found
     * @since 3.3
     */
    @Nullable
    InternalPullRequest findByComment(long commentId);

    /**
     * Attempts to find an {@link InternalPullRequest} by its {@link InternalPullRequest#getScopedId() repository-scoped
     * ID} and the ID of its scoping repository.
     * <p>
     * This method should not be confused with {@link #getById(Object)}, which retrieves pull requests by their
     * {@link InternalPullRequest#getGlobalId() globally-unique ID}.
     *
     * @param repositoryId  the ID of the repository to which the pull request will be merged
     * @param pullRequestId the repository-scoped ID for the pull request
     * @return the matching pull request, or {@code null} if no matching repository was found
     */
    @Nullable
    InternalPullRequest findByRepositoryScopedId(int repositoryId, long pullRequestId);

    /**
     * Attempts to find an {@link InternalPullRequest} by its {@link InternalPullRequestRef refs}. Only one pull request
     * may be open between any given pair of refs at a time, so this should always return either one result or none.
     * <p>
     * This check <i>does</i> consider the {@link InternalPullRequestRef#getRepository() repository} for both refs, so
     * pull requests between different repositories using the same {@link InternalPullRequestRef#getId() branch IDs}
     * will be considered distinct for the purposes of this query.
     *
     * @param from the {@link InternalPullRequest#getFromRef() from ref} for the pull request
     * @param to   the {@link InternalPullRequest#getToRef() to ref} for the pull request
     * @return the matching pull request, or {@code null} if no matching pull request was found
     */
    @Nullable
    InternalPullRequest findByRefs(@Nonnull InternalPullRequestRef from, @Nonnull InternalPullRequestRef to);

    /**
     * Finds {@link PullRequestState#MERGED unmerged} pull requests {@link InternalPullRequest#getFromRef() from} the
     * specified repository. This includes {@link PullRequestState#OPEN open} and {@link PullRequestState#DECLINED}
     * pull requests. Because the number of declined pull requests grows over time, this must be paged to ensure it
     * does not load an excessive number of pull requests into memory.
     *
     * @param repositoryId the ID of the repository from which unmerged pull requests should be found
     * @param pageRequest  describes the page of pull requests to retrieve
     * @return a page containing 0 or more unmerged pull requests from the specified repository
     */
    @Nonnull
    Page<InternalPullRequest> findUnmergedByFromRepository(int repositoryId, @Nonnull PageRequest pageRequest);

    int overwriteFromRepository(int repositoryId);

    /**
     * Searches pull requests given a criteria object to specify the search criteria, post-filtered by the provided
     * predicate to allow clients of this method to apply filters not easily implemented as a search criteria (such as
     * permission checks).
     *
     * @param criteria    the criteria to use when searching pull requests
     * @param pageRequest the page request to bound the page of results returned
     * @param predicate   the predicate clients may use to filter results
     * @return a page of containing zero or more pull requests matching both the specified criteria <i>and</i> predicate
     */
    @Nonnull
    Page<InternalPullRequest> search(@Nonnull PullRequestSearchCriteria criteria, @Nonnull PageRequest pageRequest,
                                     @Nonnull Predicate<? super InternalPullRequest> predicate);
}
