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

import com.atlassian.event.api.EventPublisher;
import com.atlassian.bitbucket.event.repository.RepositoryAccessedEvent;
import com.atlassian.bitbucket.AuthorisationException;
import com.atlassian.stash.internal.project.InternalNormalProject;
import com.atlassian.stash.internal.repository.InternalRepository;
import com.atlassian.bitbucket.repository.RepositoryService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class RepositoryAccessInterceptorTest {

    @Mock
    private EventPublisher eventPublisher;
    @InjectMocks
    private RepositoryAccessInterceptor interceptor;
    @Mock
    private RepositoryService repositoryService;
    @Mock
    private HttpServletRequest request;
    @Mock
    private HttpServletResponse response;

    @Test
    public void testIgnoresAuthorisationExceptions() throws Exception {
        doThrow(AuthorisationException.class).when(repositoryService).getBySlug(eq("PROJ"), eq("REPO"));
        when(request.getPathInfo()).thenReturn("/projects/PROJ/repos/REPO/commits");

        interceptor.preHandle(request, response, null);

        verify(repositoryService).getBySlug(eq("PROJ"), eq("REPO"));
        verifyZeroInteractions(eventPublisher);
    }

    @Test
    public void testIgnoresNonRepoUrls() throws Exception {
        ensureNotIntercepted(null);
        ensureNotIntercepted("");
        ensureNotIntercepted("/");
        ensureNotIntercepted("/projects/PROJ");
        ensureNotIntercepted("/projects/PROJ/repos");
        ensureNotIntercepted("/projects/PROJ/repos/");
    }

    @Test
    public void testInterception() throws Exception {
        testInterception("PROJ", "repo");
    }

    @Test
    public void testInterceptHyphenatedProject() throws Exception {
        testInterception("TEST-1", "repo-1");
    }

    private void ensureNotIntercepted(String url) throws Exception {
        when(request.getPathInfo()).thenReturn(url);
        interceptor.preHandle(request, response, null);
        verify(eventPublisher, never()).publish(any());
    }

    private void testInterception(String projectKey, String repositorySlug) throws Exception {
        final InternalRepository repo = new InternalRepository.Builder()
                .project(new InternalNormalProject.Builder().name(projectKey).build())
                .name(repositorySlug)
                .build();

        when(request.getPathInfo()).thenReturn("/projects/" + projectKey + "/repos/" + repositorySlug + "/browse");
        when(repositoryService.getBySlug(eq(projectKey), eq(repositorySlug))).thenReturn(repo);

        interceptor.preHandle(request, response, null);

        verify(eventPublisher, times(1)).publish(argThat(new ArgumentMatcher<RepositoryAccessedEvent>() {

            @Override
            public boolean matches(Object o) {
                return ((RepositoryAccessedEvent) o).getRepository() == repo;
            }
        }));
    }
}
