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

import com.atlassian.bitbucket.scm.git.command.checkrefformat.GitCheckRefFormat;
import com.atlassian.bitbucket.scm.git.command.checkrefformat.GitCheckRefFormatBuilder;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.CommandExitHandler;
import com.atlassian.bitbucket.scm.CommandOutputHandler;
import com.atlassian.bitbucket.scm.git.command.GitCommandExitHandler;
import com.atlassian.utils.process.BaseOutputHandler;
import com.atlassian.utils.process.ProcessException;
import com.google.common.io.ByteStreams;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;

/**
 * Provides an instance of {@link ExitHandler} and {@link CommandOutputHandler} that can be used to build a
 * {@code git check-ref-format} command that will return a boolean status indicating whether a given ref name is valid.
 *
 * @since 2.8
 * @see GitCheckRefFormat#ref(String)
 * @see GitCheckRefFormatBuilder
 */
public class CheckRefFormatCommandHandler {

    private final ExitHandler exitHandler;
    private final CommandOutputHandler<Boolean> outputHandler;

    private boolean zeroExit = true;

    public CheckRefFormatCommandHandler(@Nonnull I18nService i18nService) {
        this(i18nService, null);
    }

    public CheckRefFormatCommandHandler(@Nonnull I18nService i18nService, @Nullable Repository repository) {
        this.exitHandler = new ExitHandler(i18nService, repository);
        this.outputHandler = new IgnoringOutputHandler();
    }

    public CommandExitHandler getExitHandler() {
        return exitHandler;
    }

    public CommandOutputHandler<Boolean> getCommandOutputHandler() {
        return outputHandler;
    }

    private class ExitHandler extends GitCommandExitHandler {

        public ExitHandler(@Nonnull I18nService i18nService, @Nullable Repository repository) {
            super(i18nService, repository);
        }

        @Override
        protected void onError(String command, int exitCode, String stdErr, Throwable thrown) {
            if (exitCode != 0 || isNonZeroExitCodeException(thrown)) {
                zeroExit = false;
            } else {
                super.onError(command, exitCode, stdErr, thrown);
            }
        }
    }

    // this output handler simply consumes and ignores all the output - we only use the exit code to determine
    // the result
    private class IgnoringOutputHandler extends BaseOutputHandler implements CommandOutputHandler<Boolean> {
        @Override
        public Boolean getOutput() {
            return zeroExit;
        }

        @Override
        public void process(InputStream output) throws ProcessException {
            try {
                ByteStreams.copy(output, ByteStreams.nullOutputStream());
            } catch (IOException e) {
                throw new ProcessException(e);
            }
        }
    }
}
