package com.atlassian.stash.internal.scm.git.submodule;

import com.atlassian.bitbucket.content.Submodule;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.RepositoryCloneLinksRequest;
import com.atlassian.bitbucket.repository.RepositoryService;
import com.atlassian.bitbucket.scm.git.command.GitCommand;
import com.atlassian.bitbucket.util.NamedLink;
import com.atlassian.bitbucket.util.SimpleNamedLink;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import java.net.URI;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class DefaultGitSubmoduleHelperTest {

    @Mock
    private Repository repository;
    @Mock
    private GitSubmoduleCommandFactory gitSubmoduleCommandFactory;
    @Mock
    private GitSubmoduleUriHandler gitSubmoduleUriHandler;
    @Mock
    private RepositoryService repositoryService;

    private DefaultGitSubmoduleHelper createService() {
        return new DefaultGitSubmoduleHelper(gitSubmoduleCommandFactory, gitSubmoduleUriHandler, repositoryService);
    }

    @Test
    public void testLazy() {
        createService().getSubmodules(repository, "", "");
        verifyZeroInteractions(gitSubmoduleCommandFactory);
    }

    @Test
    public void testFindRemotes() {
        addModules(ImmutableMap.of("a/b", "ssh://git@bitbucket.org:7999/PROJECT_1/rep_1.git"));
        Submodule submodule = createService().getSubmodules(repository, "refs/heads/master", "a").getSubmodule("b", "0a943a29376f2336b78312d99e65da17048951db");
        assertNotNull("Remote must exist", submodule);
        assertEquals("b", submodule.getPath().toString());
        assertEquals("0a943a29376f2336b78312d99e65da17048951db", submodule.getContentId());
        assertNotNull(submodule.getBrowseUrl());
        assertNotNull(submodule.getRepositoryUrl());
    }

    @Test
    public void testFindRemotesNull() {
        addModules(ImmutableMap.<String, String>of());
        assertNull(createService().getSubmodules(repository, "refs/heads/master", "a").getSubmodule("b", ""));
    }

    @Test
    public void testBitbucketUrls() {
        String[] urls = new String[]{
                "https://user@bitbucket.org/user/repo.git",
                "ssh://git@bitbucket.org/user/repo.git",
                "git@bitbucket.org:user/repo.git",
        };
        for (String url : urls) {
            checkConvertUrl(url, "https://bitbucket.org/user/repo", "https://bitbucket.org/user/repo/changeset/hash/");
        }
    }

    @Test
    public void testGithubUrls() {
        String[] urls = new String[]{
                "https://user@github.com/user/repo.git",
                "ssh://git@github.com/user/repo.git",
                "git@github.com:user/repo.git",
        };
        for (String url : urls) {
            checkConvertUrl(url, "https://github.com/user/repo", "https://github.com/user/repo/tree/hash");
        }
    }

    @Test
    public void testRelativeUrls() {
        String basePath = "https://github.com/user";
        Set<NamedLink> cloneLinks = Sets.<NamedLink>newHashSet(
                new SimpleNamedLink(basePath + "/repot", "http"));
        when(repositoryService.getCloneLinks(Mockito.any(RepositoryCloneLinksRequest.class))).thenReturn(cloneLinks);

        // Simple relative path
        assertEquals("Relative path must expand to absolute path",
                createService().resolveRelativePath("../repot2.git", repository),
                basePath + "/repot2.git");

        // Simple case with ./ and ..
        assertEquals("Relative path must expand",
                createService().resolveRelativePath("./../repot2.git", repository),
                basePath + "/repot2.git");

        // Relative but not really ./
        assertEquals("Relative path must expand",
                createService().resolveRelativePath("./branches", repository),
                basePath + "/repot/branches");

        // Relative path outside of base url
        assertEquals("Relative path must expand",
                createService().resolveRelativePath("../../../interesting", repository),
                "https://github.com/../interesting");
    }

    @Test
    public void testStrip() {
        checkConvertUrl("https://user@github.com/user/repot.git", "https://github.com/user/repot", "https://github.com/user/repot/tree/hash");
    }

    @Test
    public void testInvalidUrls() {
        when(gitSubmoduleUriHandler.process(any(URI.class), anyString())).thenReturn(new SubmoduleUrls("/processed/file", ""));
        checkConvertUrl("/some/file/patch", "/processed/file", "");
    }

    private void checkConvertUrl(String url, String baseUrl, String remoteUrl) {
        SubmoduleUrls urls = createService().convertUrl(url, "hash");
        assertEquals("Failed to parse: " + url, baseUrl, urls.getRepositoryUrl());
        assertEquals("Failed to parse: " + url, remoteUrl, urls.getBrowseUrl());
    }

    private void addModules(Map<String, String> modules) {
        @SuppressWarnings("unchecked")
        GitCommand<Map<String, String>> command = mock(GitCommand.class);
        when(command.call()).thenReturn(modules);
        when(gitSubmoduleCommandFactory.getSubmodules(repository, "refs/heads/master")).thenReturn(command);
    }

}
