/**
 * This file is part of Graylog.
 *
 * Graylog is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Graylog is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Graylog.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.graylog2.rest.filter;

import org.graylog2.Configuration;
import org.graylog2.web.IndexHtmlGenerator;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class WebAppNotFoundResponseFilterTest {
    private static final String CK_METHOD_GET = "GET";
    private static final String CK_METHOD_POST = "POST";

    @Rule
    public final MockitoRule mockitoRule = MockitoJUnit.rule();

    private final Configuration configuration = new Configuration() {
        @Override
        public URI getRestListenUri() {
            return URI.create("http://example.com/api/");
        }

        @Override
        public URI getWebListenUri() {
            return URI.create("http://example.com/web/");
        }
    };

    @Mock
    private ContainerRequestContext requestContext;
    @Mock
    private ContainerResponseContext responseContext;
    @Mock
    private IndexHtmlGenerator indexHtmlGenerator;

    private WebAppNotFoundResponseFilter filter;
    private MultivaluedHashMap<String, Object> responseHeaders;

    @Before
    public void setUp() throws Exception {
        responseHeaders = new MultivaluedHashMap<>();
        when(indexHtmlGenerator.get()).thenReturn("index.html");
        when(responseContext.getHeaders()).thenReturn(responseHeaders);
        filter = new WebAppNotFoundResponseFilter(configuration, indexHtmlGenerator);
    }

    @Test
    public void filterDoesNotFilterApplicationJson() throws Exception {
        final UriInfo uriInfo = mock(UriInfo.class);
        final List<MediaType> mediaTypes = Collections.singletonList(MediaType.APPLICATION_JSON_TYPE);
        when(uriInfo.getAbsolutePath()).thenReturn(URI.create("/web/search"));
        when(requestContext.getMethod()).thenReturn(CK_METHOD_GET);
        when(requestContext.getUriInfo()).thenReturn(uriInfo);
        when(requestContext.getAcceptableMediaTypes()).thenReturn(mediaTypes);
        when(responseContext.getStatusInfo()).thenReturn(Response.Status.NOT_FOUND);

        filter.filter(requestContext, responseContext);

        verify(responseContext, never()).setEntity("index.html", new Annotation[0], MediaType.TEXT_HTML_TYPE);
    }

    @Test
    public void filterDoesFilterTextHtml() throws Exception {
        final UriInfo uriInfo = mock(UriInfo.class);
        final List<MediaType> mediaTypes = Collections.singletonList(MediaType.TEXT_HTML_TYPE);
        when(uriInfo.getAbsolutePath()).thenReturn(URI.create("/web/search"));
        when(requestContext.getMethod()).thenReturn(CK_METHOD_GET);
        when(requestContext.getUriInfo()).thenReturn(uriInfo);
        when(requestContext.getAcceptableMediaTypes()).thenReturn(mediaTypes);
        when(responseContext.getStatusInfo()).thenReturn(Response.Status.NOT_FOUND);

        filter.filter(requestContext, responseContext);

        verify(responseContext, times(1)).setEntity("index.html", new Annotation[0], MediaType.TEXT_HTML_TYPE);
    }

    @Test
    public void filterDoesFilterApplicationXhtml() throws Exception {
        final UriInfo uriInfo = mock(UriInfo.class);
        final List<MediaType> mediaTypes = Collections.singletonList(MediaType.APPLICATION_XHTML_XML_TYPE);
        when(uriInfo.getAbsolutePath()).thenReturn(URI.create("/web/search"));
        when(requestContext.getMethod()).thenReturn(CK_METHOD_GET);
        when(requestContext.getUriInfo()).thenReturn(uriInfo);
        when(requestContext.getAcceptableMediaTypes()).thenReturn(mediaTypes);
        when(responseContext.getStatusInfo()).thenReturn(Response.Status.NOT_FOUND);

        filter.filter(requestContext, responseContext);

        verify(responseContext, times(1)).setEntity("index.html", new Annotation[0], MediaType.TEXT_HTML_TYPE);
    }

    @Test
    public void filterDoesNotFilterRestApiPrefix() throws Exception {
        final UriInfo uriInfo = mock(UriInfo.class);
        final List<MediaType> mediaTypes = Collections.singletonList(MediaType.TEXT_HTML_TYPE);
        when(uriInfo.getAbsolutePath()).thenReturn(URI.create("/api/search"));
        when(requestContext.getMethod()).thenReturn(CK_METHOD_GET);
        when(requestContext.getUriInfo()).thenReturn(uriInfo);
        when(requestContext.getAcceptableMediaTypes()).thenReturn(mediaTypes);
        when(responseContext.getStatusInfo()).thenReturn(Response.Status.NOT_FOUND);

        filter.filter(requestContext, responseContext);

        verify(responseContext, never()).setEntity("index.html", new Annotation[0], MediaType.TEXT_HTML_TYPE);
    }

    @Test
    public void filterDoesNotFilterResponseStatusOk() throws Exception {
        final UriInfo uriInfo = mock(UriInfo.class);
        final List<MediaType> mediaTypes = Collections.singletonList(MediaType.TEXT_HTML_TYPE);
        when(uriInfo.getAbsolutePath()).thenReturn(URI.create("/web/search"));
        when(requestContext.getMethod()).thenReturn(CK_METHOD_GET);
        when(requestContext.getUriInfo()).thenReturn(uriInfo);
        when(requestContext.getAcceptableMediaTypes()).thenReturn(mediaTypes);
        when(responseContext.getStatusInfo()).thenReturn(Response.Status.OK);

        filter.filter(requestContext, responseContext);

        verify(responseContext, never()).setEntity("index.html", new Annotation[0], MediaType.TEXT_HTML_TYPE);
    }

    @Test
    public void filterAddsUserAgentResponseHeader() throws Exception {
        final UriInfo uriInfo = mock(UriInfo.class);
        final List<MediaType> mediaTypes = Collections.singletonList(MediaType.TEXT_HTML_TYPE);
        when(uriInfo.getAbsolutePath()).thenReturn(URI.create("/web/search"));
        when(requestContext.getMethod()).thenReturn(CK_METHOD_GET);
        when(requestContext.getUriInfo()).thenReturn(uriInfo);
        when(requestContext.getAcceptableMediaTypes()).thenReturn(mediaTypes);
        when(responseContext.getStatusInfo()).thenReturn(Response.Status.NOT_FOUND);

        filter.filter(requestContext, responseContext);

        assertThat(responseHeaders).containsEntry("X-UA-Compatible", Collections.singletonList("IE=edge"));
    }

    @Test
    public void filterDoesNotFilterPostRequests() throws Exception {
        final UriInfo uriInfo = mock(UriInfo.class);
        final List<MediaType> mediaTypes = Collections.singletonList(MediaType.TEXT_HTML_TYPE);
        when(uriInfo.getAbsolutePath()).thenReturn(URI.create("/web/nonexisting"));
        when(requestContext.getMethod()).thenReturn(CK_METHOD_POST);
        when(requestContext.getUriInfo()).thenReturn(uriInfo);
        when(requestContext.getAcceptableMediaTypes()).thenReturn(mediaTypes);
        when(responseContext.getStatusInfo()).thenReturn(Response.Status.NOT_FOUND);

        filter.filter(requestContext, responseContext);

        verify(responseContext, never()).setEntity("index.html", new Annotation[0], MediaType.TEXT_HTML_TYPE);
    }
}
