package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.AbstractWiredExecutionContextTest;
import com.atlassian.bitbucket.mesh.git.GitRepositories;
import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderFactory;
import com.atlassian.bitbucket.mesh.git.builder.updateref.GitUpdateRefSetBuilder;
import com.atlassian.bitbucket.mesh.git.exception.CommandFailedException;
import com.atlassian.bitbucket.mesh.git.hook.*;
import com.atlassian.bitbucket.mesh.git.hook.handler.AbstractGitHookHandler;
import com.atlassian.bitbucket.mesh.junit.TemporaryDirectory;
import com.atlassian.bitbucket.mesh.layout.StorageLayout;
import com.atlassian.bitbucket.mesh.test.annotation.DirtiesRepo;
import com.atlassian.bitbucket.mesh.util.ShaUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.core.io.ResourceLoader;

import javax.annotation.Nonnull;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.List;

import static org.hamcrest.Matchers.containsString;

//This test verifies that PathRepositories attached to a PathDataStore created with the hook service
//and storage layout invoke the standard hooks. It doesn't verify _all_ of the hooks; it's expected
//that if it invokes _any_ of the standard hooks it would invoke them all
public class PathDataStoreHookTest extends AbstractWiredExecutionContextTest {

    private static final String HOOK_MESSAGE = "Rejecting reference transaction";

    @Rule
    public final TemporaryDirectory tempDir = new TemporaryDirectory();

    @Resource
    private GitProcessBuilderFactory builderFactory;
    @Resource
    private GitHookService hookService;
    @Resource
    private ResourceLoader resourceLoader;
    @Resource
    private StorageLayout storageLayout;
    private PathDataStore store;

    @Before
    public void setUp() {
        store = new PathDataStore(tempDir.getRoot(), hookService, storageLayout);
        store.start();
    }

    @DirtiesRepo
    @Test
    public void testReferenceTransaction() throws IOException {
        Path repositoryDir = tempDir.newFolder("repositories", "1");
        GitRepositories.unpackArchive(resourceLoader.getResource(GitRepositories.DEFAULT), repositoryDir);

        DefaultPathRepository repository = new DefaultPathRepository(repositoryDir);
        store.attachStorage(repository);

        try (HookRequestHandle handle = hookService.registerRequest(repository, new RejectingGitHookHandler())) {
            GitUpdateRefSetBuilder builder = builderFactory.builder(repository).updateRef()
                    .set("refs/heads/rejected-branch", "0a943a29376f2336b78312d99e65da17048951db")
                    .oldValue(ShaUtils.NULL_SHA1);
            handle.configure(builder);

            CommandFailedException exception = assertThrows(CommandFailedException.class, () -> builder.build().run());
            assertThat(exception.getStderr(), containsString(HOOK_MESSAGE));
        }
    }

    private static class RejectingGitHookHandler extends AbstractGitHookHandler {

        RejectingGitHookHandler() {
            super(GitHook.REFERENCE_TRANSACTION);
        }

        @Override
        public boolean handle(@Nonnull Repository repository, @Nonnull HookRequest request,
                              @Nonnull GitHookOutputWriter outputWriter) {
            List<String> arguments = request.getArguments();
            if (arguments.size() == 1 && "prepared".equals(arguments.get(0))) {
                try {
                    outputWriter.writeStdout(HOOK_MESSAGE + "\n");
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }

                return false;
            }

            return true;
        }
    }
}
