package com.atlassian.stash.internal.web;

import com.atlassian.bitbucket.AuthorisationException;
import com.atlassian.bitbucket.NoSuchEntityException;
import com.atlassian.bitbucket.project.NoSuchProjectException;
import com.atlassian.bitbucket.repository.NoSuchRepositoryException;
import com.atlassian.bitbucket.throttle.ResourceBusyException;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.auth.AuthenticationContext;
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.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

import static java.util.Collections.singletonMap;
import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class HandlerExceptionResolverImplTest {

    private static final Object[][] TESTS = {
            {AccessDeniedException.class, HttpServletResponse.SC_UNAUTHORIZED, "bitbucket.internal.errors.error401"},
            {NoSuchRepositoryException.class, HttpServletResponse.SC_NOT_FOUND, "bitbucket.internal.errors.repo404"},
            {NoSuchProjectException.class, HttpServletResponse.SC_NOT_FOUND, "bitbucket.internal.errors.project404"},
            {NoSuchEntityException.class, HttpServletResponse.SC_NOT_FOUND, "bitbucket.internal.errors.error404"},
            {ResourceBusyException.class, HttpServletResponse.SC_SERVICE_UNAVAILABLE, "bitbucket.internal.errors.error503"},
            {Exception.class, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "bitbucket.internal.errors.error500"}
    };

    @Mock
    private ViewResolver viewResolver;
    @Mock
    private I18nService i18nService;
    @Mock
    private AuthenticationContext authenticationContext;

    private HandlerExceptionResolverImpl resolver;

    @Before
    public void setUp() throws Exception {
        resolver = new HandlerExceptionResolverImpl(viewResolver, i18nService, authenticationContext);
    }

    @Test
    public void testResolveExceptions() throws Exception {
        for (Object[] test : TESTS) {
            testResolveException(mock((Class<Exception>) test[0]), (Integer) test[1], (String) test[2]);
        }
    }

    @Test
    public void testResolveAuthorisationException() throws Exception {
        assertNull(resolver.resolveException(null, null, null, mock(AuthorisationException.class)));
    }

    @Test(expected = AccessDeniedException.class)
    public void testResolveNoSuchEntityException() throws Exception {
        resolver.resolveException(null, null, null, mock(NoSuchEntityException.class));
    }

    private void testResolveException(Exception ex, int code, String viewName) {
        final MockHttpServletResponse response = new MockHttpServletResponse();
        final ModelAndView modelAndView = resolver.resolveException(response, ex);

        assertSame(ex, modelAndView.getModel().get("error"));
        assertEquals(code, response.getStatus());
        assertEquals(viewName, modelAndView.getViewName());
    }

    @Test
    public void testHandleAccessDeniedException() throws Exception {
        final AccessDeniedException ex = new AccessDeniedException("msg");
        final View view = mock(View.class);

        when(viewResolver.resolveViewName(eq("bitbucket.internal.errors.error401"), isA(Locale.class))).thenReturn(view);
        resolver.handle(new MockHttpServletRequest(), new MockHttpServletResponse(), ex);
        verify(view).render(eq(singletonMap("error", ex)), isA(HttpServletRequest.class), isA(HttpServletResponse.class));
    }


}
