package com.atlassian.bitbucket.mesh.git.lsremote;

import com.atlassian.bitbucket.mesh.AbstractWiredTest;
import com.atlassian.bitbucket.mesh.git.GitRepositories;
import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderFactory;
import com.atlassian.bitbucket.mesh.git.builder.lsremote.GitLsRemoteBuilder;
import com.atlassian.bitbucket.mesh.git.builder.lsremote.LsRemoteStdioHandler;
import com.atlassian.bitbucket.mesh.git.ref.Branch;
import com.atlassian.bitbucket.mesh.git.ref.Ref;
import com.atlassian.bitbucket.mesh.git.ref.Tag;
import com.atlassian.bitbucket.mesh.repository.ManagedRepository;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.test.annotation.Repo;
import com.google.common.collect.ImmutableList;
import org.junit.Test;

import javax.annotation.Resource;
import java.util.List;

public class GitLsRemoteCommandTest extends AbstractWiredTest {

    private final List<Ref> EXPECTED_REFS = ImmutableList.of(
            branch("refs/heads/basic_branching", "d6edcbf924697ab811a867421dab60d954ccad99"),
            branch("refs/heads/branch_mod_merge", "6053a1eaa1c009dd11092d09a72f3c41af1b59ad"),
            branch("refs/heads/master", "0a943a29376f2336b78312d99e65da17048951db"),
            branch("refs/heads/out_of_order_branch", "2e10dd2d1d5eea9291b296e78312e8a703964a95"),
            tag("refs/tags/backdated_annotated_tag", "80aa5be5ecf39660f798858482254f7a2ae9110e",
                    "57150c54c38d6570b2fd5e6d6cfc19476de44e84"),
            tag("refs/tags/retagged_signed_tag", "9b4e781dea0769888fe270e06ad76675f73851b1",
                    "0a943a29376f2336b78312d99e65da17048951db"),
            tag("refs/tags/signed_tag", "12ebe2a58367347cd39f19f5a72f3cbec7b8f9a9",
                    "0a943a29376f2336b78312d99e65da17048951db")
    );

    private final List<Ref> EXPECTED_REFS_UNPEELED = ImmutableList.of(
            branch("refs/heads/basic_branching", "d6edcbf924697ab811a867421dab60d954ccad99"),
            branch("refs/heads/branch_mod_merge", "6053a1eaa1c009dd11092d09a72f3c41af1b59ad"),
            branch("refs/heads/master", "0a943a29376f2336b78312d99e65da17048951db"),
            branch("refs/heads/out_of_order_branch", "2e10dd2d1d5eea9291b296e78312e8a703964a95"),
            tag("refs/tags/backdated_annotated_tag", "80aa5be5ecf39660f798858482254f7a2ae9110e"),
            tag("refs/tags/retagged_signed_tag", "9b4e781dea0769888fe270e06ad76675f73851b1"),
            tag("refs/tags/signed_tag", "12ebe2a58367347cd39f19f5a72f3cbec7b8f9a9")
    );

    @Resource
    private GitProcessBuilderFactory builderFactory;
    @Repo(value = GitRepositories.MERGE)
    private Repository merge;
    @Repo
    private ManagedRepository repository;

    @Test
    public void testLsRemote() {
        assertEquals(EXPECTED_REFS, run(builderFactory.builder(repository).lsRemote()
                .repository(repository)));
    }

    @Test
    public void testLsRemoteDefaultsToContextRepository() {
        assertEquals(EXPECTED_REFS, run(builderFactory.builder(repository).lsRemote()
                .quiet(true)));
    }

    @Test
    public void testLsRemoteRefsOnly() {
        assertEquals(EXPECTED_REFS_UNPEELED, run(builderFactory.builder(repository).lsRemote()
                .refsOnly(true)));
    }

    @Test
    public void testLsRemoteWithRefPattern() {
        List<Ref> expected = ImmutableList.of(
                branch("refs/heads/basic_branching", "d6edcbf924697ab811a867421dab60d954ccad99"),
                branch("refs/heads/master", "0a943a29376f2336b78312d99e65da17048951db"),
                branch("refs/heads/out_of_order_branch", "2e10dd2d1d5eea9291b296e78312e8a703964a95"),
                tag("refs/tags/retagged_signed_tag", "9b4e781dea0769888fe270e06ad76675f73851b1"),
                tag("refs/tags/signed_tag", "12ebe2a58367347cd39f19f5a72f3cbec7b8f9a9"));
        //Use both .refs builder methods to verify that the provided patterns are applied additive
        assertEquals(expected, run(builderFactory.builder(repository).lsRemote()
                .refs("*_branch*", "refs/heads/master", null)
                .refs(ImmutableList.of("*signed_tag"))));
    }

    @Test
    public void testLsRemoteWithTargetRepository() {
        assertEquals(EXPECTED_REFS, run(builderFactory.builder(merge)
                .lsRemote()
                .quiet(true)
                .repository(repository)));
    }

    @Test
    public void testLsRemoteWithTargetRepositoryString() {
        assertEquals(EXPECTED_REFS, run(builderFactory.builder(repository)
                .lsRemote()
                .quiet(true)
                .repository("../" + repository.getLocalId())));
    }

    private static Ref branch(String refId, String objectId) {
        return new Branch.Builder()
                .id(refId)
                .latestCommit(objectId)
                .isDefault("refs/heads/master".equals(refId))
                .build();
    }

    private static List<Ref> run(GitLsRemoteBuilder builder) {
        ImmutableList.Builder<Ref> resultBuilder = ImmutableList.builder();
        LsRemoteStdioHandler handler = new LsRemoteStdioHandler(ref -> {
            resultBuilder.add(ref);
            return false;
        }, "refs/heads/master");
        builder.build(handler).run();
        return resultBuilder.build();
    }

    private static Ref tag(String refId, String objectId, String latestCommit) {
        return new Tag.Builder()
                .id(refId)
                .hash(objectId)
                .latestCommit(latestCommit)
                .build();
    }

    private static Ref tag(String refId, String latestCommit) {
        return new Tag.Builder()
                .id(refId)
                .latestCommit(latestCommit)
                .build();
    }
}
