package com.atlassian.stash.internal.auth;

import com.atlassian.bitbucket.auth.HttpAuthenticationContext;
import com.atlassian.bitbucket.auth.HttpAuthenticationFailureContext;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.i18n.SimpleI18nService;
import com.atlassian.bitbucket.scm.AuthenticationState;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

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

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.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class BasicAuthChallengeFailureHandlerTest {

    @Mock
    private HttpAuthenticationFailureContext context;
    @InjectMocks
    private BasicAuthChallengeFailureHandler handler;
    @Spy
    private I18nService i18nService = new SimpleI18nService();
    @Mock
    private HttpServletRequest request;
    @Mock
    private HttpServletResponse response;

    @Before
    public void setup() {
        when(context.getRequest()).thenReturn(request);
        when(context.getResponse()).thenReturn(response);
    }

    @Test
    public void testOptsOutOnFormAuthentication() throws Exception {
        when(context.getMethod()).thenReturn(HttpAuthenticationContext.METHOD_FORM);

        assertFalse(handler.onAuthenticationFailure(context));
    }

    @Test
    public void testOptsOutOnTokenAuthentication() throws Exception {
        when(context.getMethod()).thenReturn(HttpAuthenticationContext.METHOD_TOKEN);

        assertFalse(handler.onAuthenticationFailure(context));
    }

    @Test
    public void testOnCaptchaRequired() throws Exception {
        when(context.getMethod()).thenReturn(HttpAuthenticationContext.METHOD_BASIC);
        when(context.getAuthenticationState()).thenReturn(AuthenticationState.CAPTCHA_REQUIRED);

        assertTrue(handler.onAuthenticationFailure(context));

        verify401Challenge("bitbucket.auth.failed.captcha.required");
}

    @Test
    public void testOnFailed() throws Exception {
        when(context.getMethod()).thenReturn(HttpAuthenticationContext.METHOD_BASIC);
        when(context.getAuthenticationState()).thenReturn(AuthenticationState.NOT_AUTHENTICATED);

        assertTrue(handler.onAuthenticationFailure(context));

        verify401Challenge("bitbucket.auth.failed.credentials");
    }

    @Test
    public void testOnUnlicensed() throws Exception {
        when(context.getMethod()).thenReturn(HttpAuthenticationContext.METHOD_BASIC);
        when(context.getAuthenticationState()).thenReturn(AuthenticationState.UNLICENSED);

        assertTrue(handler.onAuthenticationFailure(context));

        verify401Challenge("bitbucket.auth.failed.unlicensed.user");
    }

    private void verify401Challenge(String messageFragment) throws IOException {
        ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);

        verify(response).sendError(eq(HttpServletResponse.SC_UNAUTHORIZED), messageCaptor.capture());
        assertTrue("Error message mismatch!\nexpected: " + messageFragment + "\nactual: " + messageCaptor.getValue(),
                messageCaptor.getValue().contains(messageFragment));

    }
}
