package com.atlassian.bitbucket.mesh.git.commit;

import com.atlassian.bitbucket.mesh.git.content.MinimalChange;
import com.atlassian.bitbucket.mesh.git.exception.CommitNotFoundException;
import com.atlassian.bitbucket.mesh.git.exception.PathNotFoundException;

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

public interface CommitManager {

    /**
     * Retrieves the specified commit.
     * <p>
     * The returned value can never be {@code null}. If the commit does not exist or, if a path is also supplied
     * and none of the reachable commits modify that path, an exception is thrown. This diverges slightly from
     * the behavior of {@code git rev-list}, which will only exit non-zero if the specified commit doesn't exist,
     * but results in more predictable behavior.
     *
     * @param parameters parameters describing the commit to retrieve
     * @return the requested commit
     * @throws CommitNotFoundException if the requested commit does not exist
     * @throws PathNotFoundException if a path was specified and none of the commits reachable from the requested
     *                               commit modified that path
     */
    @Nonnull
    Commit getCommit(@Nonnull GetCommitParameters parameters);

    /**
     * Determines the merge base (also known as the common ancestor) between a given set of commits.
     * <p>
     * When more than 2 commits are supplied, it's possible for multiple commits to be potential merge bases. If
     * that happens, all of the potential bases are considered to be equally valid and the implementation picks
     * whichever one {@code git merge-base} outputs first.
     *
     * @param parameters parameters describing the commits to calculate the merge base for
     * @return the calculated merge base, which may be {@code null} if no commits were specified or the specified
     *         commits have no commit in common
     */
    @Nullable
    String getMergeBase(@Nonnull GetMergeBaseParameters parameters);

    /**
     * Resolves commit-ish values provided on {@code parameters} to the referenced commit ID. Revisions specified on
     * {@code parameters} can be anything that the SCM can resolve to commit IDs, including branch and tag names.
     * <p>
     * The command returns a {@link Map map} from the provided revision to the resolved commit ID. If a revision could
     * not be resolved to a commit, no value is returned in the map.
     *
     * @param parameters parameters describing what commit-ishes to resolve
     * @return a {@link Map} of commit ID by commit-ish
     */
    @Nonnull
    Map<String, String> resolveCommits(@Nonnull ResolveCommitsParameters parameters);

    /**
     * Retrieves the commits described by {@code parameters} and calls
     * {@link CommitCallback#onCommit(Commit, MinimalChange)} for each matching commit. Note that a
     * {@link MinimalChange} is only provided to the callback if {@link StreamCommitsParameters#isFollowingRenames()} is
     * {@code true} and the path was renamed in the commit in question.
     *
     * @param parameters parameters describing what commits to retrieve
     * @param callback a callback to receive the matching commits
     */
    void streamCommits(@Nonnull StreamCommitsParameters parameters, @Nonnull CommitCallback callback);

    /**
     * Streams the latest commit to modify each file in the specified directory, starting from a given commit,
     * as well as the latest commit to modify the directory itself.
     * <p>
     * If the directory contains subdirectories, the latest commit to modify each of those is <i>not</i> streamed.
     * This is a compromise on costs, since getting those commits can make the processing significantly slower.
     *
     * @param parameters parameters describing the directory to stream modification data for, and the commit to
     *                   start from
     * @param callback a callback to receive the modification data
     */
    void streamLastModified(@Nonnull StreamLastModifiedParameters parameters, @Nonnull LastModifiedCallback callback);

    /**
     * Walks the commit graph and calls {@link TraversalCallback#onNode(CommitGraphNode)} for each matching commit.
     *
     * @param parameters parameters describing what commits to match when walking the commit graph
     * @param callback a callback to receive the matching commits
     */
    void traverseCommits(@Nonnull TraverseCommitsParameters parameters, @Nonnull TraversalCallback callback);
}
