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

import com.atlassian.bitbucket.mesh.git.builder.add.GitAddBuilder;
import com.atlassian.bitbucket.mesh.git.builder.archive.GitArchiveBuilder;
import com.atlassian.bitbucket.mesh.git.builder.blame.GitBlame;
import com.atlassian.bitbucket.mesh.git.builder.branch.GitBranch;
import com.atlassian.bitbucket.mesh.git.builder.catfile.GitCatFile;
import com.atlassian.bitbucket.mesh.git.builder.checkout.GitCheckoutBuilder;
import com.atlassian.bitbucket.mesh.git.builder.checkrefformat.GitCheckRefFormat;
import com.atlassian.bitbucket.mesh.git.builder.clone.GitClone;
import com.atlassian.bitbucket.mesh.git.builder.commit.GitCommitBuilder;
import com.atlassian.bitbucket.mesh.git.builder.commitgraph.GitCommitGraph;
import com.atlassian.bitbucket.mesh.git.builder.config.GitConfig;
import com.atlassian.bitbucket.mesh.git.builder.countobjects.GitCountObjectsBuilder;
import com.atlassian.bitbucket.mesh.git.builder.diff.GitDiffBuilder;
import com.atlassian.bitbucket.mesh.git.builder.diff.GitDiffTreeBuilder;
import com.atlassian.bitbucket.mesh.git.builder.fetch.GitFetchBuilder;
import com.atlassian.bitbucket.mesh.git.builder.foreachref.GitForEachRefBuilder;
import com.atlassian.bitbucket.mesh.git.builder.formatpatch.GitFormatPatchBuilder;
import com.atlassian.bitbucket.mesh.git.builder.gc.GitGcBuilder;
import com.atlassian.bitbucket.mesh.git.builder.httpbackend.GitHttpBackendBuilder;
import com.atlassian.bitbucket.mesh.git.builder.indexpack.GitIndexPackBuilder;
import com.atlassian.bitbucket.mesh.git.builder.init.GitInitBuilder;
import com.atlassian.bitbucket.mesh.git.builder.log.GitLogBuilder;
import com.atlassian.bitbucket.mesh.git.builder.lsremote.GitLsRemoteBuilder;
import com.atlassian.bitbucket.mesh.git.builder.lstree.GitLsTreeBuilder;
import com.atlassian.bitbucket.mesh.git.builder.merge.GitMerge;
import com.atlassian.bitbucket.mesh.git.builder.merge.GitMergeBase;
import com.atlassian.bitbucket.mesh.git.builder.mergetree.GitMergeTreeBuilder;
import com.atlassian.bitbucket.mesh.git.builder.notes.GitNotes;
import com.atlassian.bitbucket.mesh.git.builder.packobjects.GitPackObjectsBuilder;
import com.atlassian.bitbucket.mesh.git.builder.packrefs.GitPackRefsBuilder;
import com.atlassian.bitbucket.mesh.git.builder.prune.GitPruneBuilder;
import com.atlassian.bitbucket.mesh.git.builder.prunepacked.GitPrunePackedBuilder;
import com.atlassian.bitbucket.mesh.git.builder.push.GitPushBuilder;
import com.atlassian.bitbucket.mesh.git.builder.rebase.GitRebaseBuilder;
import com.atlassian.bitbucket.mesh.git.builder.receivepack.GitReceivePackBuilder;
import com.atlassian.bitbucket.mesh.git.builder.remote.GitRemote;
import com.atlassian.bitbucket.mesh.git.builder.repack.GitRepackBuilder;
import com.atlassian.bitbucket.mesh.git.builder.reset.GitResetBuilder;
import com.atlassian.bitbucket.mesh.git.builder.revert.GitRevertBuilder;
import com.atlassian.bitbucket.mesh.git.builder.revlist.GitRevListBuilder;
import com.atlassian.bitbucket.mesh.git.builder.status.GitStatusBuilder;
import com.atlassian.bitbucket.mesh.git.builder.symbolicref.GitSymbolicRef;
import com.atlassian.bitbucket.mesh.git.builder.tag.GitTag;
import com.atlassian.bitbucket.mesh.git.builder.updateref.GitUpdateRef;
import com.atlassian.bitbucket.mesh.git.builder.uploadarchive.GitUploadArchiveBuilder;
import com.atlassian.bitbucket.mesh.git.builder.uploadpack.GitUploadPackBuilder;
import com.atlassian.bitbucket.mesh.git.builder.version.GitVersionBuilder;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;

/**
 * A builder for creating {@link GitProcess}es, which allows both free-form and type-safe use.
 * <p>
 * Type-safe builders are not provided for all of the commands supported by the {@code git} binary, and the builders
 * that are provided may not expose properties for all of the different arguments or argument combinations the command
 * offers. Free-form and type-safe builder semantics <i>may not be mixed</i>. If a type-safe builder does not exist for
 * the desired command, or it does not support the desired arguments, the free-form builder must be used <i>instead</i>.
 * <p>
 * For type-safe builders provided, the {@code git} command associated with the builder is used to name the interface
 * and the method. Note that some commands accept arguments which, when provided, enable the use of other arguments, or
 * trigger the command to produce significantly different output. For such commands, like {@link #blame() blame} and
 * {@link #catFile() cat-file}, an intermediary interface is returned to allow the caller to choose "mode" the command
 * will run in. Once a mode has been chosen, a builder will be returned exposing arguments supported for that mode.
 */
@NotThreadSafe
public interface GitProcessBuilder extends GitProcessBuilderSupport<GitProcessBuilder> {

    //-------------------------------------------------------------------------
    //Type-safe builders
    //-------------------------------------------------------------------------

    /**
     * @return a builder for the {@code git add} command
     */
    @Nonnull
    GitAddBuilder add();

    /**
     * @return a builder for the {@code git archive} command
     */
    @Nonnull
    GitArchiveBuilder archive();

    /**
     * @return a builder for the {@code git blame} command
     */
    @Nonnull
    GitBlame blame();

    /**
     * @return a builder for the {@code git branch} command
     */
    @Nonnull
    GitBranch branch();

    /**
     * @return a builder for the {@code git cat-file} command
     */
    @Nonnull
    GitCatFile catFile();

    /**
     * @return a builder for constructing {@code git checkout} commands
     */
    @Nonnull
    GitCheckoutBuilder checkout();

    /**
     * @return a builder for the {@code git check-ref-format} command
     */
    @Nonnull
    GitCheckRefFormat checkRefFormat();

    /**
     * @return a builder for the {@code git clone} command
     */
    @Nonnull
    GitClone clone();

    /**
     * @return a builder for the {@code git commit} command
     */
    @Nonnull
    GitCommitBuilder commit();

    /**
     * @return a builder for the {@code git commit-graph} command
     */
    @Nonnull
    GitCommitGraph commitGraph();

    /**
     * @return a builder for the {@code git config} command
     */
    @Nonnull
    GitConfig config();

    /**
     * @return a builder for the {@code git count-objects} command
     */
    @Nonnull
    GitCountObjectsBuilder countObjects();

    /**
     * @return a builder for the {@code git diff} command
     */
    @Nonnull
    GitDiffBuilder diff();

    /**
     * @return a builder for the {@code git diff-tree} command
     */
    @Nonnull
    GitDiffTreeBuilder diffTree();

    /**
     * @return a builder for the {@code git fetch} command
     */
    @Nonnull
    GitFetchBuilder fetch();

    /**
     * @return a builder for the {@code git for-each-ref} command
     */
    @Nonnull
    GitForEachRefBuilder forEachRef();

    /**
     * @return a builder for the {@code git format-patch} command
     */
    @Nonnull
    GitFormatPatchBuilder formatPatch();

    /**
     * @return a builder for the {@code git gc} command
     */
    @Nonnull
    GitGcBuilder gc();

    /**
     * @return a builder for constructing {@code git http-backend} commands
     */
    @Nonnull
    GitHttpBackendBuilder httpBackend();

    /**
     * @return a builder for constructing {@code git index-pack} commands
     */
    @Nonnull
    GitIndexPackBuilder indexPack();

    /**
     * @return a builder for the {@code git init} command
     */
    @Nonnull
    GitInitBuilder init();

    /**
     * @return a builder for the {@code git log} command
     */
    @Nonnull
    GitLogBuilder log();

    /**
     * @return a builder for the {@code git ls-tree} command
     */
    @Nonnull
    GitLsTreeBuilder lsTree();

    /**
     * @return a builder for the {@code git ls-remote} command
     * @since 1.3
     */
    @Nonnull
    GitLsRemoteBuilder lsRemote();

    /**
     * @return a builder for the {@code git merge} command
     */
    @Nonnull
    GitMerge merge();

    /**
     * @return a builder for the {@code git merge-base} command
     */
    @Nonnull
    GitMergeBase mergeBase();

    /**
     * @return a builder for the {@code git merge-tree} command
     * @since 2.1
     */
    @Nonnull
    GitMergeTreeBuilder mergeTree();

    /**
     * @return a builder for the {@code git notes} command
     */
    @Nonnull
    GitNotes notes();

    /**
     * @return a builder for the {@code git pack-objects} command
     */
    @Nonnull
    GitPackObjectsBuilder packObjects();

    /**
     * @return a builder for constructing {@code git pack-refs} commands
     */
    @Nonnull
    GitPackRefsBuilder packRefs();

    /**
     * @return a builder for constructing {@code git prune} commands
     */
    @Nonnull
    GitPruneBuilder prune();

    /**
     * @return a builder for the {@code git prune-packed} command
     */
    @Nonnull
    GitPrunePackedBuilder prunePacked();

    /**
     * @return a builder for the {@code git push} command
     */
    @Nonnull
    GitPushBuilder push();

    /**
     * @return a builder for constructing {@code git rebase} commands
     */
    @Nonnull
    GitRebaseBuilder rebase();

    /**
     * @return a builder for constructing {@code git receive-pack} commands
     */
    @Nonnull
    GitReceivePackBuilder receivePack();

    /**
     * @return a builder for constructing {@code git remote} commands
     */
    @Nonnull
    GitRemote remote();

    /**
     * @return a builder for constructing {@code git repack} commands
     */
    @Nonnull
    GitRepackBuilder repack();

    /**
     * @return a builder for the {@code git reset} command
     */
    @Nonnull
    GitResetBuilder reset();

    /**
     * @return a builder for the {@code git revert} command
     *
     * @since 2.1
     */
    @Nonnull
    GitRevertBuilder revert();

    /**
     * @return a builder for the {@code git rev-list} command
     */
    @Nonnull
    GitRevListBuilder revList();

    /**
     * @return a builder for the {@code git status} command
     */
    @Nonnull
    GitStatusBuilder status();

    /**
     * @return a builder for the {@code git symbolic-ref} command
     */
    @Nonnull
    GitSymbolicRef symbolicRef();

    /**
     * @return a builder for the {@code git tag} command
     */
    @Nonnull
    GitTag tag();

    /**
     * @return a builder for the {@code git update-ref} command
     */
    @Nonnull
    GitUpdateRef updateRef();

    /**
     * @return a builder for the {@code git upload-archive} command
     */
    @Nonnull
    GitUploadArchiveBuilder uploadArchive();

    /**
     * @return a builder for constructing {@code git upload-pack} commands
     */
    @Nonnull
    GitUploadPackBuilder uploadPack();

    /**
     * @return a builder for the {@code git version} command
     */
    @Nonnull
    GitVersionBuilder version();

    //-------------------------------------------------------------------------
    //Free-form builder
    //-------------------------------------------------------------------------

    /**
     * Appends the provided argument, <i>with leading and trailing whitespace trimmed</i>
     * <p>
     * Note: Arguments must be provided <i>one per invocation</i>. There is no need to quote strings which include
     * spaces; spaces in arguments <i>are not interpreted</i>. They are passed <i>as-is</i> to the command.
     *
     * @param argument the argument to append to the command
     * @return {@code this}
     * @throws IllegalArgumentException if the provided {@code argument} is empty or blank
     * @throws NullPointerException if the provided {@code argument} is {@code null}
     */
    @Nonnull
    GitProcessBuilder argument(String argument);

    /**
     * Appends the provided arguments, <i>with leading and trailing whitespace trimmed</i>
     * <p>
     * Note: There is no need to quote strings which include spaces; spaces in arguments <i>are not interpreted</i>.
     * They are passed <i>as-is</i> to the command.
     *
     * @param arguments the arguments to append to the command
     * @return {@code this}
     * @throws IllegalArgumentException if any of the provided {@code arguments} are empty or blank
     * @throws NullPointerException if any of the provided {@code arguments} are {@code null}
     */
    @Nonnull
    GitProcessBuilder arguments(Iterable<String> arguments);

    /**
     * Inserts the provided argument, <i>with leading and trailing whitespace trimmed</i>, immediately after the
     * specified anchor if the anchor can be found. Otherwise, the argument is appended at the end.
     * <p>
     * Note: Arguments must be provided <i>one per invocation</i>. There is no need to quote strings which include
     * spaces; spaces in arguments <i>are not interpreted</i>. They are passed <i>as-is</i> to the command.
     *
     * @param anchor   the argument serving as the anchor for the new argument
     * @param argument the argument to add immediately after the specified anchor
     * @return {@code this}
     * @throws IllegalArgumentException if the provided {@code argument} is empty or blank
     * @throws NullPointerException if the provided {@code argument} is {@code null}
     */
    @Nonnull
    GitProcessBuilder argumentAfter(String anchor, String argument);

    /**
     * Inserts the provided argument, <i>with leading and trailing whitespace trimmed</i>, at the specified index.
     * <p>
     * Note: Arguments must be provided <i>one per invocation</i>. There is no need to quote strings which include
     * spaces; spaces in arguments <i>are not interpreted</i>. They are passed <i>as-is</i> to the command.
     *
     * @param index    the index at which the argument should be inserted
     * @param argument the argument to insert at the specified index
     * @return {@code this}
     * @throws IllegalArgumentException if the provided {@code argument} is empty or blank
     * @throws IndexOutOfBoundsException if the provided {@code index} is out of bounds
     * @throws NullPointerException if the provided {@code argument} is {@code null}
     */
    @Nonnull
    GitProcessBuilder argumentAt(int index, String argument);

    /**
     * Inserts the provided argument, <i>with leading and trailing whitespace trimmed</i>, immediately before the
     * specified anchor if the anchor can be found. Otherwise, the argument is appended at the end.
     * <p>
     * Note: Arguments must be provided <i>one per invocation</i>. There is no need to quote strings which include
     * spaces; spaces in arguments <i>are not interpreted</i>. They are passed <i>as-is</i> to the command.
     *
     * @param anchor   the argument serving as the anchor for the new argument
     * @param argument the argument to add immediately before the specified anchor
     * @return {@code this}
     * @throws IllegalArgumentException if the provided {@code argument} is empty or blank
     * @throws NullPointerException if the provided {@code argument} is {@code null}
     */
    @Nonnull
    GitProcessBuilder argumentBefore(String anchor, String argument);

    /**
     * Clears any arguments which have been set.
     *
     * @return {@code this}
     */
    @Nonnull
    GitProcessBuilder clearArguments();

    /**
     * Sets the command to execute.
     * <p>
     * Most SCMs, such as {@code git}, {@code hg} or {@code svn}, have a single unified binary which expects the
     * command to run as its first argument. This property separates that special command argument from the others,
     * allowing other arguments to be {@link #clearArguments() cleared} without clearing the command as well.
     *
     * @param command the SCM command to execute
     * @return {@code this}
     * @throws IllegalArgumentException if the provided {@code command} is empty or blank
     * @throws NullPointerException if the provided {@code command} is {@code null}
     */
    @Nonnull
    GitProcessBuilder command(@Nonnull String command);

    /**
     * Appends a commit-ish argument to the command. This validates that the value does not start with a {@code -},
     * which could potentially be interpreted as a command line option instead of a commit identifier.
     * <p>
     * <b>Definition</b>: A "commit-ish" is a value that can coerced by Git to identify a single commit. A commit's
     * SHA is obviously a commit-ish value, but so are branch and tag names, as well as other other single revision
     * specifiers like {@code HEAD^} or {@code HEAD~12}.
     *
     * @param value the commit-ish value to append
     * @return {@code this}
     */
    @Nonnull
    GitProcessBuilder commitish(@Nonnull String value);

    /**
     * Appends the provided argument, <i>verbatim</i>
     * <p>
     * Note: Arguments must be provided <i>one per invocation</i>. There is no need to quote strings which include
     * spaces; spaces in arguments <i>are not interpreted</i>. They are passed <i>as-is</i> to the command.
     *
     * @param argument the argument to append to the command
     * @return {@code this}
     * @throws NullPointerException if the provided {@code argument} is {@code null}
     */
    @Nonnull
    GitProcessBuilder rawArgument(String argument);

    /**
     * Inserts the provided argument, <i>verbatim</i>, immediately after the specified anchor if the anchor can be
     * found. Otherwise, the argument is appended at the end.
     * <p>
     * Note: Arguments must be provided <i>one per invocation</i>. There is no need to quote strings which include
     * spaces; spaces in arguments <i>are not interpreted</i>. They are passed <i>as-is</i> to the command.
     *
     * @param anchor   the argument serving as the anchor for the new argument
     * @param argument the argument to add immediately after the specified anchor
     * @return {@code this}
     * @throws NullPointerException if the provided {@code argument} is {@code null}
     */
    @Nonnull
    GitProcessBuilder rawArgumentAfter(String anchor, String argument);

    /**
     * Inserts the provided argument, <i>verbatim</i>, at the specified index.
     * <p>
     * Note: Arguments must be provided <i>one per invocation</i>. There is no need to quote strings which include
     * spaces; spaces in arguments <i>are not interpreted</i>. They are passed <i>as-is</i> to the command.
     *
     * @param index    the index at which the argument should be inserted
     * @param argument the argument to insert at the specified index
     * @return {@code this}
     * @throws IndexOutOfBoundsException if the provided {@code index} is out of bounds
     * @throws NullPointerException if the provided {@code argument} is {@code null}
     */
    @Nonnull
    GitProcessBuilder rawArgumentAt(int index, String argument);

    /**
     * Inserts the provided argument, <i>verbatim</i>, immediately before the specified anchor if the anchor can be
     * found. Otherwise, the argument is appended at the end.
     * <p>
     * Note: Arguments must be provided <i>one per invocation</i>. There is no need to quote strings which include
     * spaces; spaces in arguments <i>are not interpreted</i>. They are passed <i>as-is</i> to the command.
     *
     * @param anchor   the argument serving as the anchor for the new argument
     * @param argument the argument to add immediately before the specified anchor
     * @return {@code this}
     * @throws NullPointerException if the provided {@code argument} is {@code null}
     */
    @Nonnull
    GitProcessBuilder rawArgumentBefore(String anchor, String argument);

    /**
     * Appends a tree-ish argument to the command. This validates that the value does not start with a {@code -},
     * which could potentially be interpreted as a command line option instead of a tree identifier.
     * <p>
     * <b>Definition</b>: A "tree-ish" is a value that can be coerced by Git to identify a single tree. A tree's
     * SHA is obviously a tree-ish value, but so are commits (which have a root tree) and branches/tags (which
     * identify commits).
     *
     * @param value the tree-ish value to append
     * @return {@code this}
     */
    @Nonnull
    GitProcessBuilder treeish(@Nonnull String value);
}
