package com.atlassian.stash.internal.scm.git.protocol.ssh;

import com.atlassian.bitbucket.help.HelpPathService;
import com.atlassian.bitbucket.hook.HookRequestHandle;
import com.atlassian.bitbucket.hook.HookService;
import com.atlassian.bitbucket.hook.HookUtils;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.request.RequestContext;
import com.atlassian.bitbucket.request.RequestManager;
import com.atlassian.bitbucket.scm.git.GitConstants;
import com.atlassian.bitbucket.scm.git.GitScmConfig;
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory;
import com.atlassian.bitbucket.scm.git.command.GitScmCommandBuilder;
import com.atlassian.bitbucket.scm.git.protocol.AnalyzingInputStream;
import com.atlassian.bitbucket.scm.git.protocol.ReceivePackRequestAnalyzer;
import com.atlassian.bitbucket.scm.ssh.ExitCodeCallback;
import com.atlassian.bitbucket.scm.ssh.SshInputHandler;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.stash.internal.scm.git.GitWriteTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import java.io.InputStream;
import java.io.OutputStream;

public class SshReceivePack extends GitSshScmRequest {

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

    private final ReceivePackRequestAnalyzer analyzer;
    private final HelpPathService helpPathService;
    private final HookService hookService;

    private HookRequestHandle hookHandle;
    private boolean success;

    public SshReceivePack(@Nonnull Repository repository, @Nonnull GitCommandBuilderFactory builderFactory,
                          @Nonnull GitScmConfig config, @Nonnull EventPublisher eventPublisher,
                          @Nonnull HelpPathService helpPathService, @Nonnull HookService hookService,
                          @Nonnull I18nService i18nService, @Nonnull ExitCodeCallback exitCodeCallback,
                          @Nonnull RequestManager requestManager, @Nonnull InputStream in, @Nonnull OutputStream out,
                          @Nonnull OutputStream err, @Nonnull GitWriteTracker writeTracker) {
        super(repository, true, builderFactory, config, eventPublisher, i18nService, exitCodeCallback, requestManager,
                GitSshUtils.COMMAND_RECEIVE_PACK, in, out, err, writeTracker);

        this.helpPathService = helpPathService;
        this.hookService = hookService;

        analyzer = new ReceivePackRequestAnalyzer();
    }

    @Override
    public void handleRequest() {
        hookHandle = hookService.registerRequest(repository.getId());
        try {
            super.handleRequest();
        } finally {
            hookHandle.close();
        }
    }

    @Override
    protected void addLabels(@Nonnull RequestContext requestContext) {
        if (success && analyzer.isUpdate()) {
            requestContext.addLabel(GitConstants.LABEL_PUSH);

            if (hookHandle.isCalled() && !hookHandle.isAccepted()) {
                // ref changes were provided but not accepted
                requestContext.addLabel(GitConstants.LABEL_REJECTED);
            }
        } else {
            // no ref changes were requested - means that the client was up to date or an error occurred. Either way,
            // all the server did was advertize its refs.
            requestContext.addLabel(GitConstants.LABEL_REFS);
        }
    }

    @Override
    protected void configureCommandBuilder(GitScmCommandBuilder commandBuilder) {
        super.configureCommandBuilder(commandBuilder);

        // override the InputHandler to intercept and analyze the request
        commandBuilder.inputHandler(new SshInputHandler(bufferSize, new AnalyzingInputStream(in, analyzer)));

        HookUtils.configure(hookHandle, commandBuilder);
    }

    @Override
    protected void onSuccess() {
        success = true;

        if (analyzer.isUpdate() && !hookHandle.isCalled()) {
            log.warn("{}: Git hooks have not been called. Please verify that the hooks are configured correctly - " +
                            "see {} for details",
                    repository, helpPathService.getPageUrl("bitbucket.kb.scm.hooks.not.executing"));
        }
    }
}
