package com.atlassian.stash.internal.auth;

import com.atlassian.bitbucket.auth.HttpAuthenticationContext;
import com.atlassian.bitbucket.auth.HttpAuthenticationFailureContext;
import com.atlassian.bitbucket.nav.NavBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import javax.servlet.ServletException;
import java.io.IOException;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class RedirectingAuthenticationFailureHandlerTest {

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private NavBuilder navBuilder;

    @Mock
    private NavBuilder.Login login;

    @InjectMocks
    private RedirectingAuthenticationFailureHandler handler;

    @Test
    public void checkRedirectionWithNextParam() throws ServletException, IOException {
        HttpAuthenticationFailureContext context = mockContext(HttpAuthenticationContext.METHOD_TOKEN, "foo");
        assertTrue(handler.onAuthenticationFailure(context));
        verify(navBuilder.login().withParam(eq("next"), eq("foo"))).buildRelative();
    }

    @Test
    public void checkRedirectionWithoutNextParam() throws ServletException, IOException {
        HttpAuthenticationFailureContext context = mockContextWithParams(HttpAuthenticationContext.METHOD_TOKEN);
        assertTrue(handler.onAuthenticationFailure(context));
        verify(navBuilder.login()).buildRelative();
    }

    @Test
    public void checkRedirectionWithAllSupportedParams() throws ServletException, IOException {
        when(navBuilder.login()).thenReturn(login);
        when(login.withParam(anyString(), anyString())).thenReturn(login);
        when(login.buildRelative()).thenReturn("http://success");

        HttpAuthenticationFailureContext context = mockContextWithParams(HttpAuthenticationContext.METHOD_TOKEN,
                "next", "foo",
                "permission", "bar",
                "embedded", "maybe",
                "unsupported", "yes");

        assertTrue(handler.onAuthenticationFailure(context));

        verify(navBuilder).login();
        verify(login).withParam("next", "foo");
        verify(login).withParam("permission", "bar");
        verify(login).withParam("embedded", "maybe");
        verify(login).buildRelative();
        verifyNoMoreInteractions(login);
    }

    @Test
    public void checkRedirectionIgnoreBasicAuth() throws ServletException, IOException {
        HttpAuthenticationFailureContext context = mockContext(HttpAuthenticationContext.METHOD_BASIC, null);
        assertFalse(handler.onAuthenticationFailure(context));
        verifyZeroInteractions(navBuilder);
    }

    private HttpAuthenticationFailureContext mockContext(String method, String nextUrl) {
        return mockContextWithParams(method, "next", nextUrl);
    }

    private HttpAuthenticationFailureContext mockContextWithParams(String method, String... nameValuePairs) {
        HttpAuthenticationFailureContext context = mock(HttpAuthenticationFailureContext.class, Mockito.RETURNS_DEEP_STUBS);
        when(context.getMethod()).thenReturn(method);
        for (int i = 0; i < nameValuePairs.length; i += 2) {
            when(context.getRequest().getParameter(eq(nameValuePairs[i]))).thenReturn(nameValuePairs[i + 1]);
        }
        return context;
    }

}
