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

import com.atlassian.bitbucket.mesh.git.GitProcessCompletionHandler;
import com.atlassian.bitbucket.mesh.git.exception.NonFastForwardUpdateException;
import com.atlassian.bitbucket.mesh.git.porcelain.FetchRefParameters;
import com.atlassian.bitbucket.mesh.process.ProcessSummary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;

import static java.util.Objects.requireNonNull;

/**
 * Inspects the stderr of a {@code git fetch} command (as part of a merge - either an explicit merge by the user or a
 * merge used for generating diffs) and throws a {@link NonFastForwardUpdateException} when it detects the ref
 * update was rejected.
 */
public class FetchCompletionHandler extends GitProcessCompletionHandler {

    private static final String NON_FAST_FORWARD = "(non-fast-forward)";
    private static final String REJECTED = " ! [rejected]";

    private static final Logger log = LoggerFactory.getLogger(FetchCompletionHandler.class);

    private final boolean forced;
    private final String ref;

    public FetchCompletionHandler(@Nonnull FetchRefParameters parameters) {
        forced = requireNonNull(parameters, "parameters").isForced();
        ref = requireNonNull(parameters.getTarget(), "target");
    }

    /**
     * Evaluates the contents of stderr. If stderr does not suggest the ref update failed then default processing is
     * performed as defined in {@link GitProcessCompletionHandler#evaluateStderr(ProcessSummary, String)}}. If stderr
     * does suggest a failed update then how it responds depends on the current fetch mode:
     * <p>
     * If the fetch was {@link #forced} then it should never fail. One of the primary reasons to use {@code git fetch}
     * instead of {@code git push} is that hooks <i>are not run</i>, so it can't be a hook blocking the fetch. To try
     * and help support diagnose the odd diff behaviour the user will see, we log a very specific message and then
     * throw a {@link NonFastForwardUpdateException}.
     * <p>
     * Otherwise, it is highly likely the repository's target branch was changed during the merge process and so we
     * throw a {@link NonFastForwardUpdateException} for the infrastructure higher up to handle as appropriate.
     *
     * @param summary the summary of the completed process
     * @param stderr  the stderr contents to inspect
     * @throws NonFastForwardUpdateException if a {@link #forced} fetch was rejected, or if a normal fetch
     *                                               was rejected because it was not fast-forward
     */
    @Override
    protected void evaluateStderr(@Nonnull ProcessSummary summary, @Nonnull String stderr) {
        if (stderr.contains(REJECTED)) {
            if (forced) {
                log.error("Merge: A forced update to {} was rejected by git.\n\"{}\" failed with:\n{}",
                        ref, summary.getCommandLine(), stderr);
                throw new NonFastForwardUpdateException(ref);
            }
            if (stderr.contains(NON_FAST_FORWARD)) {
                throw new NonFastForwardUpdateException(ref);
            }
        }

        super.evaluateStderr(summary, stderr);
    }
}
