package com.atlassian.bitbucket.mesh.git.builder.revlist;

import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderSupport;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcRevListOptions;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.Instant;

/**
 * Describes common {@code rev-list} functionality, which is shared by {@code log}.
 */
public interface GitRevCoreBuilder<B extends GitRevCoreBuilder<B>> extends GitProcessBuilderSupport<B> {

    /**
     * Controls use of {@code --all}
     *
     * @param value {@code true} to include all refs under {@code refs/} to the rev-list
     * @return {@code this}
     */
    @Nonnull
    B all(boolean value);

    /**
     * Applies the provided {@link RpcRevListOptions} to the builder, potentially setting many of the individual
     * properties on this builder in a single call.
     *
     * @param value the options to apply
     * @return {@code this}
     */
    @Nonnull
    B applyOptions(@Nonnull RpcRevListOptions value);

    /**
     * Controls use of {@code --boundary}. When a range of commits is provided (whether symmetric or not), "boundary"
     * commits are where the walk reaches the first excluded commit as it does its traversal. Even for a simple range
     * like "master..feature-branch" (or "feature-branch ^master", written another way), multiple boundary commits may
     * exist. Boundary commits are prefixed with a {@code -}, to differentiate them from reachable commits.
     *
     * @param value {@code true} to output boundary commits, which are the first
     * @return {@code this}
     * @see #leftRight(boolean) --left-right
     */
    @Nonnull
    B boundary(boolean value);

    /**
     * Controls use of {@code --branches}
     *
     * @param value {@code true} to include all branches in the rev-list, {@code false} otherwise
     * @return {@code this}
     */
    @Nonnull
    B branches(boolean value);

    /**
     * Clears any {@link #files(Iterable) files} which have been added, allowing the builder to be reused to construct
     * multiple commands for traversals at different paths.
     *
     * @return {@code this}
     */
    @Nonnull
    B clearFiles();

    /**
     * Clears any {@link #revs(Iterable) revs} which have been added, allowing the builder to be reused to construct
     * multiple commands for traversals over different commit ranges.
     *
     * @return {@code this}
     */
    @Nonnull
    B clearRevs();

    @Nonnull
    B file(String value);

    /**
     * @param values a collection of file paths, within the repository, to limit returned commits
     * @return {@code this}
     */
    @Nonnull
    B files(Iterable<String> values);

    /**
     *
     * @param value  a file path, within the repository, to limit returned commits
     * @param values additional file paths, within the repository, to limit returned commits
     * @return {@code this}
     */
    @Nonnull
    B files(String value, String... values);

    @Nonnull
    B format(String value);

    @Nonnull
    B ignoreMissing(boolean value);

    /**
     * Controls use of {@code --left-only}, which will return only commits reachable from the left-hand side of a
     * symmetric range. When using {@link #leftRight(boolean) --left-right}, these would be the commits prefixed
     * with a {@code <}.
     * <p>
     * This setting is <i>mutually exclusive</i> with {@link #rightOnly(boolean) --right-only}. Setting one will
     * overwrite the other.
     *
     * @param value {@code true} to return only those commits reachable from the left-hand side of a symmetric range;
     *              otherwise, {@code false} to return all commits
     * @return {@code this}
     * @see #rightOnly(boolean) --right-only
     */
    @Nonnull
    B leftOnly(boolean value);

    /**
     * Controls use of {@code --left-right}, which is used to mark which side of a symmetric range a given commit is
     * reachable from. When enabled, {@code git rev-list} will prefix commits with {@code <} if they are reachable
     * from the left-hand side and with {@code >} if they are reachable from the right-hand side. When combined with
     * {@link #boundary(boolean) --boundary}, boundary commits are prefixed with {@code -}.
     * <p>
     * Given two branches, A and B, providing "A...B" as the {@link #rev(String) rev} will produce a symmetric range
     * that can be used to show which commits are reachable from A but not B, and reachable from B but not A. It's the
     * same as providing "A", "B", and "^(merge-base A B)".
     *
     * @param value {@code true} to mark which side of a symmetric range each commit is reachable from; otherwise,
     *              {@code false} to output matching commits from either side of the range normally
     * @return {@code this}
     * @see #boundary(boolean) --boundary
     */
    @Nonnull
    B leftRight(boolean value);

    /**
     * Specifies the max number of commits to output.
     *
     * @param value the max number of commits
     * @return {@code this}
     */
    @Nonnull
    B limit(int value);

    /**
     * Specifies {@code --merges}, {@code --no-merges} or {@code --no-min-parents}, to control whether
     * {@code git rev-list} should include merge commits in its output.
     *
     * @param value the merge mode for the command
     * @return {@code this}
     */
    @Nonnull
    B merges(@Nonnull GitRevListMerges value);

    /**
     * Specifies {@code --date-order} or {@code --topo-order}, to control the {@code git rev-list} commit order.
     *
     * @param value the order for the command
     * @return {@code this}
     */
    @Nonnull
    B order(@Nonnull GitRevListOrder value);

    /**
     * Controls use of {@code --parents}
     *
     * @param value {@code true} to include all parents in the rev-list, {@code false} otherwise
     * @return {@code this}
     */
    @Nonnull
    B parents(boolean value);

    @Nonnull
    B rev(String value);

    /**
     * Allows defining revisions to include and/or exclude using varargs-style syntax. Revisions can be specified
     * using: ref names, fully qualified or short; SHA1s, full or short so long as they're unique; or using relative
     * notations like {@code HEAD~5} or {@code HEAD^^^} or {@code HEAD@{2}}. Revisions to exclude can be specified
     * by prepending {@code ^}, like {@code ^master}.
     *
     * @param values a collection of revisions to include and/or exclude, to control returned commits
     * @return {@code this}
     */
    @Nonnull
    B revs(Iterable<String> values);

    /**
     * Allows defining revisions to include and/or exclude using varargs-style syntax. Revisions can be specified
     * using: ref names, fully qualified or short; SHA1s, full or short so long as they're unique; or using relative
     * notations like {@code HEAD~5} or {@code HEAD^^^} or {@code HEAD@{2}}. Revisions to exclude can be specified
     * by prepending {@code ^}, like {@code ^master}.
     *
     * @param value  a revision to include or exclude, to control returned commits
     * @param values additional revisions to include and/or exclude, to control returned commits
     * @return {@code this}
     */
    @Nonnull
    B revs(String value, String... values);

    /**
     * Controls use of {@code --right-only}, which will return only commits reachable from the right-hand side of a
     * symmetric range. When using {@link #leftRight(boolean) --left-right}, these would be the commits prefixed
     * with a {@code >}.
     * <p>
     * This setting is <i>mutually exclusive</i> with {@link #leftOnly(boolean) --left-only}. Setting one will
     * overwrite the other.
     *
     * @param value {@code true} to return only those commits reachable from the right-hand side of a symmetric range;
     *              otherwise, {@code false} to return all commits
     * @return {@code this}
     * @see #leftOnly(boolean)
     */
    @Nonnull
    B rightOnly(boolean value);

    /**
     * Show commits more recent than a specific date.
     *
     * @param value limits to commits newer than the {@link Instant} provided
     * @return {@code this}
     */
    @Nonnull
    B since(@Nullable Instant value);

    /**
     * Controls use of {@code --skip}.
     *
     * @param value the number of commits to skip before starting to provide output
     * @return {@code this}
     */
    @Nonnull
    B skip(int value);

    /**
     * Controls whether {@code git rev-list} attempts to read additional {@link #revs} and/or {@link #files} from
     * {@code stdin}. By default, {@code git rev-list} does not read from {@code stdin} so files and revs must be
     * provided on the command line. Providing too many revs and/or files can result in errors forking the process,
     * however; in such cases they should be provided using {@code stdin} instead.
     *
     * @param value {@code true} if additional revs and/or files will be provided on {@code stdin}; otherwise,
     *              {@code false} if everything will be provided on the command line
     * @return {@code this}
     */
    B stdin(boolean value);

    /**
     * Controls use of {@code --tags}
     *
     * @param value {@code true} to include all tags in the rev-list, {@code false} otherwise
     * @return {@code this}
     */
    @Nonnull
    B tags(boolean value);

    /**
     * Specifies {@code --no-walk} or {@code --do-walk}, to control whether {@code git rev-list} traverses commit
     * ancestry.
     * <p>
     * Note: When using {@link GitRevListWalk#NO_WALK --no-walk}, excludes <i>are not supported</i>. This a limitation
     * of {@code git rev-list}. If excludes are provided {@code --no-walk} is <i>ignored</i>.
     *
     * @param value the walk mode for the command
     * @return {@code this}
     */
    @Nonnull
    B walk(@Nonnull GitRevListWalk value);
}
