package com.atlassian.stash.internal.web.filters;

import com.atlassian.bitbucket.nav.NavBuilder;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.RepositoryCloneLinksRequest;
import com.atlassian.bitbucket.repository.RepositoryService;
import com.atlassian.bitbucket.scm.AvailableScm;
import com.atlassian.bitbucket.scm.ScmService;
import com.atlassian.bitbucket.user.DummySecurityService;
import com.atlassian.bitbucket.user.SecurityService;
import com.atlassian.bitbucket.util.SimpleNamedLink;
import com.atlassian.soy.renderer.SoyTemplateRenderer;
import com.atlassian.stash.internal.scm.SimpleAvailableScm;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletResponse;
import java.util.Collections;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNotNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class GoGetFilterTest {

    private static final String MODULE_KEY = "com.atlassian.bitbucket.server.bitbucket-web:server-soy-templates";
    private static final String TEMPLATE_KEY = "bitbucket.internal.page.goget";

    private MockHttpServletRequest request;
    private MockHttpServletResponse response;

    @Mock
    private FilterChain chain;
    @InjectMocks
    private GoGetFilter filter;
    @Mock
    private NavBuilder navBuilder;
    @Mock
    private Repository repository;
    @Mock
    private RepositoryService repositoryService;
    @Mock
    private ScmService scmService;
    @Spy
    private SecurityService securityService = new DummySecurityService();
    @Mock
    private SoyTemplateRenderer soyTemplateRenderer;

    @Before
    public void setup() {
        request = new MockHttpServletRequest();
        response = new MockHttpServletResponse();

        when(navBuilder.buildAbsolute()).thenReturn("http://localhost");
        when(repository.getScmId()).thenReturn("git");

        when(repositoryService.getBySlug(eq("project"), eq("repo"))).thenReturn(repository);
    }

    @Test
    public void testFilterWithValidUrl() throws Exception {
        when(repositoryService.getCloneLinks(isNotNull(RepositoryCloneLinksRequest.class)))
                .thenReturn(Collections.singleton(new SimpleNamedLink("http://localhost/scm/project/repo.git", "http")));

        request.setServerName("localhost");
        request.setRequestURI("/project/repo");
        request.setParameter("go-get", "1");

        filter.doFilter(request, response, chain);

        assertEquals(HttpServletResponse.SC_OK, response.getStatus());
        verify(soyTemplateRenderer).render(response.getWriter(), MODULE_KEY, TEMPLATE_KEY, ImmutableMap.<String, Object>of(
                "importPrefix", "localhost/project/repo",
                "repoRoot", "http://localhost/scm/project/repo.git",
                "vcs", "git"));
    }

    @Test
    public void testFilterWithValidUrlButNonexistentRepositoryAndOneScm() throws Exception {
        when(scmService.getAvailable()).thenReturn(Collections.singleton(
                new SimpleAvailableScm.Builder("git").name("Git").build()));

        request.setServerName("localhost");
        request.setRequestURI("/project/nonexistent"); //Won't match the RepositoryService training from setup
        request.setParameter("go-get", "1");

        filter.doFilter(request, response, chain);

        assertEquals(HttpServletResponse.SC_OK, response.getStatus());
        verify(soyTemplateRenderer).render(response.getWriter(), MODULE_KEY, TEMPLATE_KEY, ImmutableMap.<String, Object>of(
                "importPrefix", "localhost/project/nonexistent",
                "repoRoot", "http://localhost/scm/project/nonexistent.git",
                "vcs", "git"));
    }

    @Test
    public void testFilterWithValidUrlButNonexistentRepositoryAndTwoScms() throws Exception {
        ImmutableSet<AvailableScm> scms = ImmutableSet.of(
                new SimpleAvailableScm.Builder("git").name("Git").build(),
                new SimpleAvailableScm.Builder("hg").name("Mercurial").build());
        when(scmService.getAvailable()).thenReturn(scms);

        //This request URI will hash to Mercurial. The result _should_ be consistent, but any changes to the
        //String.hashCode() implementation might break that.
        request.setServerName("localhost");
        request.setRequestURI("/project/nonexistent"); //Won't match the RepositoryService training from setup
        request.setParameter("go-get", "1");

        filter.doFilter(request, response, chain);

        assertEquals(HttpServletResponse.SC_OK, response.getStatus());
        verify(soyTemplateRenderer).render(response.getWriter(), MODULE_KEY, TEMPLATE_KEY, ImmutableMap.<String, Object>of(
                "importPrefix", "localhost/project/nonexistent",
                "repoRoot", "http://localhost/scm/project/nonexistent",
                "vcs", "hg"));
    }

    @Test
    public void testFilterWithValidUrlWithContextPath() throws Exception {
        when(repositoryService.getCloneLinks(isNotNull(RepositoryCloneLinksRequest.class)))
                .thenReturn(Collections.singleton(new SimpleNamedLink("http://localhost/bitbucket/scm/project/repo.git", "http")));

        request.setServerName("localhost");
        request.setContextPath("/bitbucket");
        request.setRequestURI("/bitbucket/project/repo");
        request.setParameter("go-get", "1");

        filter.doFilter(request, response, chain);

        assertEquals(HttpServletResponse.SC_OK, response.getStatus());
        verify(soyTemplateRenderer).render(response.getWriter(), MODULE_KEY, TEMPLATE_KEY, ImmutableMap.<String, Object>of(
                "importPrefix", "localhost/bitbucket/project/repo",
                "repoRoot", "http://localhost/bitbucket/scm/project/repo.git",
                "vcs", "git"));
    }

    @Test
    public void testFilterWithValidUrlWithSubDirectory() throws Exception {
        when(repositoryService.getCloneLinks(isNotNull(RepositoryCloneLinksRequest.class)))
                .thenReturn(Collections.singleton(new SimpleNamedLink("http://localhost/scm/project/repo.git", "http")));

        request.setServerName("localhost");
        request.setRequestURI("/project/repo/sub/directory");
        request.setParameter("go-get", "1");

        filter.doFilter(request, response, chain);

        assertEquals(HttpServletResponse.SC_OK, response.getStatus());
        verify(soyTemplateRenderer).render(response.getWriter(), MODULE_KEY, TEMPLATE_KEY, ImmutableMap.<String, Object>of(
                "importPrefix", "localhost/project/repo",
                "repoRoot", "http://localhost/scm/project/repo.git",
                "vcs", "git"));
    }

    @Test
    public void testFilterWithInvalidUrlNoProject() throws Exception {
        request.setServerName("localhost");
        request.setRequestURI("/");
        request.setParameter("go-get", "1");

        filter.doFilter(request, response, chain);

        assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
        verify(soyTemplateRenderer).render(response.getWriter(), MODULE_KEY, TEMPLATE_KEY, Collections.<String, Object>emptyMap());
    }

    @Test
    public void testFilterWithInvalidUrlNoRepo() throws Exception {
        request.setServerName("localhost");
        request.setRequestURI("/project");
        request.setParameter("go-get", "1");

        filter.doFilter(request, response, chain);

        assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
        verify(soyTemplateRenderer).render(response.getWriter(), MODULE_KEY, TEMPLATE_KEY, Collections.<String, Object>emptyMap());
    }

    @Test
    public void testFilterWithInvalidUrlNoRepoWithContextPath() throws Exception {
        request.setServerName("localhost");
        request.setContextPath("/stash");
        request.setRequestURI("/stash/project");
        request.setParameter("go-get", "1");

        filter.doFilter(request, response, chain);

        assertEquals(HttpServletResponse.SC_BAD_REQUEST, response.getStatus());
        verify(soyTemplateRenderer).render(response.getWriter(), MODULE_KEY, TEMPLATE_KEY, Collections.<String, Object>emptyMap());
    }

    @Test
    public void testFilterWithNoGoGetParameter() throws Exception {
        filter.doFilter(request, response, chain);

        verify(chain).doFilter(request, response);
    }
}
