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

import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.git.command.GitCommandExitHandler;
import com.google.common.base.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Exit handler for git fetch operations. Since git fetch outputs progress information on stderr, the output handler
 * needs to parse the error stream and separate the progress information from the errors. These real errors are then
 * provided to this exit handler through a supplier.
 *
 * @since 4.1
 */
public class FetchExitHandler extends GitCommandExitHandler {

    public static final Pattern PATTERN_LOCK_FAILED = Pattern.compile("fatal: Unable to create '.+/(refs/heads/[^']+)\\.lock': File exists\\.");
    public static final Pattern PATTERN_REMOTE_REF = Pattern.compile("fatal: Couldn't find remote ref (refs/heads/.*)");

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

    private final Supplier<String> stdError;

    public FetchExitHandler(I18nService i18nService, Repository repository, Supplier<String> stdError) {
        super(i18nService, repository);

        this.stdError = stdError;
    }

    @Override
    public void onExit(@Nonnull String command, int exitCode, String stdErr, Throwable thrown) {
        //Pass the supplied real stderror through
        super.onExit(command, exitCode, stdError.get(), thrown);
    }

    @Override
    protected boolean isError(@Nonnull String command, int exitCode, String stdErr, Throwable thrown) {
        if (exitCode != 0 || isNonZeroExitCodeException(thrown)) {
            return true;
        }

        StringBuilder refWarnings = new StringBuilder();
        Matcher matcher = PATTERN_LOCK_FAILED.matcher(stdErr);
        while (matcher.find()) {
            refWarnings.append("\n\t").append(matcher.group(1)).append(" is locked by another process");
        }

        matcher = PATTERN_REMOTE_REF.matcher(stdErr);
        while (matcher.find()) {
            refWarnings.append("\n\t").append(matcher.group(1)).append(" no longer exists");
        }
        if (refWarnings.length() > 0) {
            log.warn("{}: not all refs could not be fetched; {}", repository, refWarnings.toString());

            return false;
        }

        return super.isError(command, exitCode, stdErr, thrown);
    }
}
