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

import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilder;
import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderFactory;
import com.atlassian.bitbucket.mesh.git.builder.merge.*;
import com.atlassian.bitbucket.mesh.git.porcelain.WorkTreeParameters;
import com.atlassian.bitbucket.mesh.repository.Repository;

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

public abstract class AbstractMergeStrategy<B extends GitMergeBuilderSupport<B>> implements MergeStrategy {

    private final GitProcessBuilderFactory builderFactory;
    private final GitMergeFastForward fastForward;

    protected AbstractMergeStrategy(GitProcessBuilderFactory builderFactory, GitMergeFastForward fastForward) {
        this.builderFactory = builderFactory;
        this.fastForward = fastForward;
    }

    /**
     * @param builder the builder to customize
     * @return the provided builder, unchanged
     */
    @Nonnull
    @Override
    public WorkTreeParameters.Builder customizeWorkTree(@Nonnull WorkTreeParameters.Builder builder) {
        return builder;
    }

    /**
     * Implemented in derived strategies to choose the appropriate mode from the {@link GitMerge merge selector} and
     * return it.
     *
     * @param merge the merge selector, used to create a mode-appropriate builder
     * @return the mode-appropriate builder for this strategy
     */
    protected abstract B chooseMode(GitMerge merge);

    /**
     * Creates a {@link GitProcessBuilder builder} with no associated {@link Repository repository} which will
     * build commands that run in the specified working directory.
     *
     * @param workDir the working directory for commands built with the returned builder
     * @return a new builder with its working directory set
     */
    protected GitProcessBuilder builder(Path workDir) {
        return builderFactory.builder().workDir(workDir);
    }

    /**
     * Creates a {@link GitMergeBuilderSupport merge builder} which will run in the specified working directory,
     * and applies common flags based on the {@link MergeParameters merge parameters}.
     *
     * @param workingDir the working directory for the {@code git merge} command
     * @param parameters parameters describing the merge being performed
     * @return a new merge builder with its working directory and common flags set
     */
    protected B mergeBuilder(Path workingDir, MergeParameters parameters) {
        return chooseMode(builder(workingDir).merge())
                .allowUnrelatedHistories(parameters.isAllowUnrelated())
                .author(parameters.getAuthor())
                .committer(parameters.getCommitter())
                .commit(parameters.getFrom().getLatestCommit())
                .completionHandler(new MergeCompletionHandler())
                .ff(fastForward)
                .verify(GitMergeVerify.OFF);
    }
}
