package com.atlassian.bitbucket.internal.scm.git.lfs.rest;

import com.atlassian.bitbucket.AuthorisationException;
import com.atlassian.bitbucket.auth.AuthenticationException;
import com.atlassian.bitbucket.internal.scm.git.lfs.BadBatchRequestException;
import com.atlassian.bitbucket.internal.scm.git.lfs.GitLfsDisabledException;
import com.atlassian.bitbucket.internal.scm.git.lfs.http.HttpScmErrorUtils;
import com.atlassian.bitbucket.project.NoSuchProjectException;
import com.atlassian.bitbucket.repository.NoSuchRepositoryException;
import com.atlassian.bitbucket.rest.exception.UnhandledExceptionMapperHelper;
import com.atlassian.bitbucket.throttle.ResourceBusyException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.util.Arrays;
import java.util.Collection;

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

@RunWith(Parameterized.class)
public class GitLfsExceptionMapperTest {

    private static final String TEST_MESSAGE = "Error String";
    private static final String TEST_FORMATTED_MESSAGE = String.format(HttpScmErrorUtils.ERROR_FORMAT, TEST_MESSAGE);

    private final Class<? extends Exception> exceptionClass;
    private final int expectedStatus;
    private GitLfsExceptionMapper instance;

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][]{
                {AuthenticationException.class, HttpServletResponse.SC_UNAUTHORIZED},
                {AuthorisationException.class, HttpServletResponse.SC_FORBIDDEN},
                {BadBatchRequestException.class, HttpServletResponse.SC_BAD_REQUEST},
                {GitLfsDisabledException.class, HttpServletResponse.SC_NOT_IMPLEMENTED},
                {NoSuchProjectException.class, HttpServletResponse.SC_NOT_FOUND},
                {NoSuchRepositoryException.class, HttpServletResponse.SC_NOT_FOUND},
                {ResourceBusyException.class, HttpServletResponse.SC_SERVICE_UNAVAILABLE},
                {RuntimeException.class, HttpServletResponse.SC_INTERNAL_SERVER_ERROR}
        });
    }

    @Before
    public void setup() throws Exception {
        instance = new GitLfsExceptionMapper(mock(UnhandledExceptionMapperHelper.class));
    }

    public GitLfsExceptionMapperTest(Class<? extends Exception> exceptionClass, int expectedStatus) {
        this.exceptionClass = exceptionClass;
        this.expectedStatus = expectedStatus;
    }

    @Test
    public void testToResponse() throws Exception {
        // Setup
        Exception ex = mock(exceptionClass);
        when(ex.getLocalizedMessage()).thenReturn(TEST_MESSAGE);
        when(ex.getMessage()).thenReturn(TEST_MESSAGE);

        // Run
        Response resp = instance.toResponse(ex);

        // Verify
        assertEquals(expectedStatus, resp.getStatus());
        assertTrue(resp.getEntity() instanceof String);
        String entity = (String) resp.getEntity();
        assertEquals(TEST_FORMATTED_MESSAGE, entity);
    }

    /**
     * Tests an exception where there is no mapping for the exception's type, but the type of
     * the cause does have a mapping to a specific response code
     */
    @Test
    public void testToResponseViaCause() throws Exception {
        // Setup
        Exception cause = mock(exceptionClass);
        when(cause.getLocalizedMessage()).thenReturn(TEST_MESSAGE);
        when(cause.getMessage()).thenReturn(TEST_MESSAGE);

        WebApplicationException ex = mock(WebApplicationException.class);
        when(ex.getCause()).thenReturn(cause);

        // Run
        Response resp = instance.toResponse(ex);

        // Verify
        assertEquals(expectedStatus, resp.getStatus());
        assertTrue(resp.getEntity() instanceof String);
    }
}