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

import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderSupport;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
 * A builder for constructing {@code git checkout} commands.
 * <p>
 * {@code git checkout} has 6 different usages, per its documentation:
 * <ul>
 *     <li>{@code git checkout [-q] [-f] [-m] [&lt;branch&gt;]}</li>
 *     <li>{@code git checkout [-q] [-f] [-m] --detach [&lt;branch&gt;]}</li>
 *     <li>{@code git checkout [-q] [-f] [-m] [--detach] &lt;commit&gt;}</li>
 *     <li>{@code git checkout [-q] [-f] [-m] [[-b|-B|--orphan] &lt;new_branch&gt;] [&lt;start_point&gt;]}</li>
 *     <li>{@code git checkout [-f|--ours|--theirs|-m|--conflict=&lt;style&gt;] [&lt;tree-ish&gt;] [--] &lt;paths&gt;...}</li>
 *     <li>{@code git checkout [-p|--patch] [&lt;tree-ish&gt;] [--] [&lt;paths&gt;...]}</li>
 * </ul>
 * Of these 6 usages, only the first four are supported by this builder. The {@code -m} flag, and usages which are
 * related to merging/handling conflicts or checkout out limited paths are not supported.
 */
public interface GitCheckoutBuilder extends GitProcessBuilderSupport<GitCheckoutBuilder> {

    /**
     * Specifies an <i>existing branch</i> to checkout. To <i>create</i> a branch and check it out, use
     * {@link #branch(String, String)} or {@link #branch(String, String, GitCheckoutCreateBranchMode)} instead.
     * <p>
     * The value provided here may also be a commit ID or a tag. In either case, it is the same as calling
     * {@link #detach(String)}: The appropriate <i>commit</i> will be checked out and the work tree will be
     * left in a "detached {@code HEAD}" state.
     *
     * @param value the name of the existing branch to checkout
     * @return {@code this}
     */
    @Nonnull
    GitCheckoutBuilder branch(@Nonnull String value);

    /**
     * Specifies the name of a <i>new branch</i> to create and checkout in a single operation. To checkout an
     * <i>existing</i> branch, use {@link #branch(String)} instead.
     * <p>
     * Calling this method is the same as {@link #branch(String, String, GitCheckoutCreateBranchMode) branch(value,
     * startPoint, CREATE)}.
     *
     * @param value      the name for the new branch
     * @param startPoint the commit ID or ref name to use as the starting point for the new branch, which may be
     *                   {@code null} to use {@code HEAD}
     * @return {@code this}
     */
    @Nonnull
    GitCheckoutBuilder branch(@Nonnull String value, @Nullable String startPoint);

    /**
     * Specifies the name of a <i>new branch</i> to create and checkout in a single operation, as well as the
     * mode to use when creating it. To checkout an <i>existing</i> branch, use {@link #branch(String)} instead.
     *
     * @param value      the name for the new branch
     * @param startPoint the commit ID or ref name to use as the starting point for the new branch, which may be
     *                   {@code null} to use {@code HEAD}
     * @param mode       the mode to use when creating the new branch
     * @return {@code this}
     */
    @Nonnull
    GitCheckoutBuilder branch(@Nonnull String value, @Nullable String startPoint,
                              @Nonnull GitCheckoutCreateBranchMode mode);

    /**
     * Checkout the specified commit and put the work tree into a "detached {@code HEAD}" state.
     * <p>
     * When a repository is detached, {@code HEAD} refers to a specific commit ID, rather than using a symbolic
     * ref to specify a branch. New commits may still be authored, but they will start out unreferenced and will
     * be available for garbage collection.
     * <p>
     * The value provided may be any ref name (a branch or a tag), or a specific commit ID (SHA-1). If a commit
     * ID is provided, whether by calling this method or {@link #branch(String)}, the work tree will always be
     * in a "detached" state.
     *
     * @param value the commit to detach from, which may be specified by ID or ref name
     * @return {@code this}
     */
    @Nonnull
    GitCheckoutBuilder detach(@Nonnull String value);

    /**
     * @param value {@code true} to discard any local changes; otherwise, {@code false} to cause the checkout to
     *              fail if there are uncommitted changes
     * @return {@code this}
     */
    @Nonnull
    GitCheckoutBuilder force(boolean value);

    /**
     * @param value {@code true} to suppress output; otherwise, {@code false}
     * @return {@code this}
     */
    @Nonnull
    GitCheckoutBuilder quiet(boolean value);

    /**
     * Sets a flag indicating whether a reflog should also be created when creating a new branch. This setting has
     * no effect when checking out an existing branch.
     *
     * @param value {@code true} to create a reflog when creating a new branch; otherwise, {@code false}
     * @return {@code this}
     */
    @Nonnull
    GitCheckoutBuilder reflog(boolean value);
}
