package com.atlassian.stash.internal.spring.security;

import com.atlassian.bitbucket.event.request.RequestStartedEvent;
import com.atlassian.stash.internal.nav.NavBuilderImpl;
import com.atlassian.bitbucket.request.RequestContext;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.security.core.AuthenticationException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import java.net.URISyntaxException;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class StashLoginUrlAuthenticationEntryPointTest {

    @Mock
    private AuthenticationException authenticationException;
    @Mock
    private HttpServletRequest request;
    @Mock
    private HttpServletResponse response;
    @Mock
    private RequestContext requestContext;

    @Mock
    ApplicationPropertiesService applicationPropertiesService;

    private NavBuilderImpl navBuilder;

    private StashLoginUrlAuthenticationEntryPoint stashLoginURLAuthenticationEntryPoint;

    @Before
    public void setup() throws URISyntaxException {
        when(applicationPropertiesService.getBaseUrl()).thenReturn(new URI("http://localhost:7990/bitbucket"));
        when(requestContext.getRawRequest()).thenReturn(request);
        navBuilder = new NavBuilderImpl(applicationPropertiesService);

        stashLoginURLAuthenticationEntryPoint = new StashLoginUrlAuthenticationEntryPoint("/login", navBuilder);
    }

    @Test
    public void testLoginURLShouldHaveCurrentRequestURLAsPartOfItsQueryString() throws Exception {
        testDetermineURLToUseForThisRequest("/stash/projects/QA/repos/pr-merge-conflict/pull-requests", null, "/login?next=/projects/QA/repos/pr-merge-conflict/pull-requests");
    }

    @Test
    public void testLoginURLShouldNotHaveCurrentRequestURLAsPartOfItsQueryStringIFItsAPOSTRequest() throws Exception {
        when(request.getRequestURI()).thenReturn("/stash/projects/QA/repos/pr-merge-conflict/pull-requests");
        when(request.getQueryString()).thenReturn(null);
        when(request.getMethod()).thenReturn("POST");
        RequestStartedEvent event = new RequestStartedEvent(this, requestContext);
        navBuilder.onRequestStarted(event);
        String loginURL = stashLoginURLAuthenticationEntryPoint.determineUrlToUseForThisRequest(request, response, authenticationException);
        assertEquals("/login", loginURL);
    }

    @Test
    public void testLoginURLShouldHaveCurrentRequestURLAndQueryStringAsPartOfItsQueryString() throws Exception {
        testDetermineURLToUseForThisRequest("/stash/projects/QA/repos/pr-merge-conflict/pull-requests", "create", "/login?next=/projects/QA/repos/pr-merge-conflict/pull-requests?create");
    }

    @Test
    public void testDetermineUrlToUseForThisRequestShouldNotThrowExceptionIfCurrentURLIsBaseURL() throws Exception {
        testDetermineURLToUseForThisRequest("/stash", null, "/login");
    }

    @Test
    public void testDetermineUrlToUseForThisRequestShouldRemoveMVCAddedBySpring() throws Exception {
        testDetermineURLToUseForThisRequest("/stash/mvc/projects/QA/repos/pr-merge-conflict/pull-requests", null, "/login?next=/projects/QA/repos/pr-merge-conflict/pull-requests");
    }

    private void testDetermineURLToUseForThisRequest(String requestURL, String queryString, String expectedLoginURL) {
        when(request.getRequestURI()).thenReturn(requestURL);
        when(request.getQueryString()).thenReturn(queryString);
        when(request.getMethod()).thenReturn("GET");
        when(request.getContextPath()).thenReturn("/stash");
        RequestStartedEvent event = new RequestStartedEvent(this, requestContext);
        navBuilder.onRequestStarted(event);
        String loginURL = stashLoginURLAuthenticationEntryPoint.determineUrlToUseForThisRequest(request, response, authenticationException);
        assertEquals(expectedLoginURL, loginURL);
    }

}
