package com.atlassian.stash.internal.scm.git.command.merge;

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.CommandErrorHandler;
import com.atlassian.bitbucket.scm.CommandExitHandler;
import com.atlassian.bitbucket.scm.CommandOutputHandler;
import com.atlassian.bitbucket.scm.git.GitScmConfig;
import com.atlassian.bitbucket.scm.git.command.GitCommand;
import com.atlassian.bitbucket.scm.git.command.GitScmCommandBuilder;
import com.atlassian.bitbucket.scm.git.command.merge.GitMergeBuilderSupport;
import com.atlassian.bitbucket.scm.git.command.merge.GitMergeFastForward;
import com.atlassian.bitbucket.util.Version;
import com.atlassian.stash.internal.scm.git.command.AbstractGitCommandBuilder;
import com.google.common.base.MoreObjects;

import javax.annotation.Nonnull;

/**
 * @since 2.1
 */
public abstract class AbstractGitMergeBuilder<B extends GitMergeBuilderSupport<B>>
        extends AbstractGitCommandBuilder<B>
        implements GitMergeBuilderSupport<B> {

    /**
     * Version 2.9, used when applying {@link #allowUnrelatedHistories(boolean)} to prevent adding the argument
     * for versions of Git which do not support it.
     */
    private static final Version VERSION_2_9 = new Version(2, 9);

    protected final I18nService i18nService;
    protected final Repository repository;

    private final GitScmConfig config;

    protected GitMergeFastForward ff;

    private boolean allowUnrelatedHistories;
    private String commit;
    private boolean quiet;
    private boolean useDefaultExitHandler;

    public AbstractGitMergeBuilder(GitScmCommandBuilder builder, GitScmConfig config,
                                   I18nService i18nService, Repository repository) {
        super(builder.command("merge"));

        this.config = config;
        this.i18nService = i18nService;
        this.repository = repository;

        ff = GitMergeFastForward.DEFAULT;
        useDefaultExitHandler = true;
    }

    /**
     * Introduced in Git 2.9, {@code --allow-unrelated-histories} allows {@code git merge} to merge commits
     * which have no common ancestor. <i>This was always allowed by {@code git merge} prior to 2.9.</i> As
     * part of adding the flag, that default behavior was changed in {@code git merge}, which now rejects
     * merges between unrelated commits unless this flag is specified.
     * <p>
     * Since the minimum required Git version is currently (4.8) only Git 1.8, this flag cannot be added
     * directly to the {@link GitMergeBuilderSupport} API, because it would not work consistently on all
     * supported versions of Git.
     *
     * @param value {@code true} to allow {@code git merge} to merge commits which have no common ancestor;
     *              otherwise, {@code false} to reject such merges ({@code false} is the default)
     * @return {@code this}
     */
    @Nonnull
    public B allowUnrelatedHistories(boolean value) {
        allowUnrelatedHistories = value;

        return self();
    }

    @Nonnull
    @Override
    public GitCommand<Void> build() {
        return buildWithLoggingHandler();
    }

    @Nonnull
    @Override
    public <T> GitCommand<T> build(@Nonnull CommandOutputHandler<T> outputHandler) {
        if (useDefaultExitHandler) {
            builder.exitHandler(new MergeCommandExitHandler(i18nService, repository));
        }

        return super.build(outputHandler);
    }

    @Nonnull
    @Override
    public B commit(String value) {
        commit = value;

        return self();
    }

    @Nonnull
    @Override
    public B defaultExitHandler() {
        useDefaultExitHandler = true;

        return self();
    }

    @Nonnull
    @Override
    public B errorHandler(@Nonnull CommandErrorHandler value) {
        builder.errorHandler(value);

        return self();
    }

    @Nonnull
    @Override
    public B exitHandler(@Nonnull CommandExitHandler value) {
        super.exitHandler(value);

        useDefaultExitHandler = false;

        return self();
    }

    @Nonnull
    @Override
    public B ff(GitMergeFastForward value) {
        ff = MoreObjects.firstNonNull(value, GitMergeFastForward.DEFAULT);

        return self();
    }

    @Nonnull
    @Override
    public B quiet(boolean value) {
        quiet = value;

        return self();
    }

    @Override
    protected void applyArguments() {
        if (allowUnrelatedHistories && config.getVersion().compareTo(VERSION_2_9) > -1) {
            builder.argument("--allow-unrelated-histories");
        }
        if (quiet) {
            builder.argument("--quiet");
        }
        builder.argument(commit);
    }
}
