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

import com.atlassian.bitbucket.mesh.git.GitAgent;
import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderFactory;
import com.atlassian.bitbucket.mesh.git.builder.merge.GitMerge;
import com.atlassian.bitbucket.mesh.git.builder.merge.GitMergeBuilder;
import com.atlassian.bitbucket.mesh.git.builder.merge.GitMergeFastForward;
import com.atlassian.bitbucket.mesh.git.builder.merge.MergeStdioHandler;

import javax.annotation.Nonnull;
import java.nio.file.Path;

/**
 * A {@link MergeStrategy merge strategy} which supports a configurable {@link GitMergeFastForward fast-forward}
 * requirement. Depending on the fast-forward mode specified during construction, the merge will be:
 * <ul>
 *     <li>{@link GitMergeFastForward#ONLY Strictly fast-forward}</li>
 *     <li>{@link GitMergeFastForward#ON Fast-forward if possible}, but willing to produce a merge commit if the
 *     if the target commit is not an ancestor of the source</li>
 *     <li>{@link GitMergeFastForward#OFF Strictly non-fast-forward}, always producing a merge commit</li>
 * </ul>
 */
public class DefaultMergeStrategy extends AbstractMergeStrategy<GitMergeBuilder> {

    public DefaultMergeStrategy(GitProcessBuilderFactory builderFactory, GitMergeFastForward fastForward) {
        super(builderFactory, fastForward);
    }

    /**
     * Merges the specified commit into {@link GitAgent#HEAD HEAD} in the provided
     * {@code workTree}, committing the merge as the specified {@code author} with the provided {@code message}.
     * <p>
     * {@code git merge} is invoked with {@code --no-ff} to ensure it always creates a new merge commit even if the
     * specified commit is fast-forward from the current {@link GitAgent#HEAD} revision.
     * {@code --log} is used to trigger git to append short descriptions of each commit included in the merge to the
     * provided {@code message}.
     *
     * @param parameters parameters describing the overall merge being performed
     * @param workTree   the temporary folder containing a non-bare clone for performing the merge
     */
    @Override
    public void merge(@Nonnull MergeParameters parameters, @Nonnull Path workTree) {
        //At this point we have a clone that offers a work tree that git merge is willing to work with, so we use that
        //to merge in the requested commit(s). This will lazily checkout any files being merged, but no more than that.
        //In other words, if the branch affects 42 files out of a repository with 27,000 files in it, only those 42
        //files will be checked out into the work tree. By the same token, though, if the branch affects thousands of
        //files, they'll all be checked out here.
        MergeStdioHandler handler = new MergeStdioHandler();
        GitMergeBuilder builder = mergeBuilder(workTree, parameters)
                .log(parameters.getLog())
                .message(parameters.getMessage());

        parameters.configureAndRun(handler.build(builder));
    }

    @Override
    protected GitMergeBuilder chooseMode(GitMerge merge) {
        return merge.normal();
    }
}
