/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2015 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * http://glassfish.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.jersey.tests.e2e.server;

import java.util.logging.Level;
import java.util.logging.LogRecord;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.LocalizationMessages;
import org.glassfish.jersey.spi.ExtendedExceptionMapper;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;

import org.junit.Assert;
import org.junit.Test;
import static org.junit.Assert.fail;

/**
 * @author Miroslav Fuksa
 *
 */
public class ExtendedExceptionMapperTest extends JerseyTest {

    @Override
    protected Application configure() {
        set(TestProperties.RECORD_LOG_LEVEL, Level.FINE.intValue());

        return new ResourceConfig(
                TestResource.class,
                ExceptionMapperA.class,
                ExceptionMapperB.class,
                ExceptionMapperC.class,
                ExceptionMapperD.class,
                ExceptionMapperE.class,
                ExceptionMapperX.class
        );
    }

    public static class ExceptionA extends RuntimeException {
        public ExceptionA(String message) {
            super(message);
        }
    }

    public static class ExceptionB extends ExceptionA {
        public ExceptionB(String message) {
            super(message);
        }
    }

    public static class ExceptionC extends ExceptionB {
        public ExceptionC(String message) {
            super(message);
        }
    }

    public static class ExceptionD extends ExceptionC {
        public ExceptionD(String message) {
            super(message);
        }
    }

    public static class ExceptionE extends ExceptionD {
        public ExceptionE(String message) {
            super(message);
        }
    }

    public static class ExceptionX extends RuntimeException {
        public ExceptionX(String message) {
            super(message);
        }
    }


    public static class ExceptionMapperA implements ExtendedExceptionMapper<ExceptionA> {

        @Override
        public boolean isMappable(ExceptionA exception) {
            return exception.getMessage().substring(2).contains("A");
        }

        @Override
        public Response toResponse(ExceptionA exception) {
            return Response.ok("A").build();
        }
    }

    public static class ExceptionMapperB implements ExtendedExceptionMapper<ExceptionB> {

        @Override
        public boolean isMappable(ExceptionB exception) {
            return exception.getMessage().substring(2).contains("B");
        }

        @Override
        public Response toResponse(ExceptionB exception) {
            return Response.ok("B").build();
        }
    }

    public static class ExceptionMapperC implements ExceptionMapper<ExceptionC> {

        @Override
        public Response toResponse(ExceptionC exception) {
            return Response.ok("C").build();
        }
    }

    public static class ExceptionMapperD implements ExtendedExceptionMapper<ExceptionD> {

        @Override
        public boolean isMappable(ExceptionD exception) {
            return exception.getMessage().substring(2).contains("D");
        }

        @Override
        public Response toResponse(ExceptionD exception) {
            return Response.ok("D").build();
        }
    }

    public static class ExceptionMapperE implements ExtendedExceptionMapper<ExceptionE> {

        @Override
        public boolean isMappable(ExceptionE exception) {
            return exception.getMessage().substring(2).contains("E");
        }

        @Override
        public Response toResponse(ExceptionE exception) {
            return Response.ok("E").build();
        }
    }

    public static class ExceptionMapperX implements ExtendedExceptionMapper<ExceptionX> {

        @Override
        public boolean isMappable(ExceptionX exception) {
            return exception.getMessage().substring(2).contains("X");
        }

        @Override
        public Response toResponse(ExceptionX exception) {
            return Response.ok("X").build();
        }
    }

    @Path("resource")
    public static class TestResource {
        @POST
        public String post(String e) {
            if (e.charAt(0) == 'A') {
                throw new ExceptionA(String.valueOf(e));
            } else if (e.charAt(0) == 'B') {
                throw new ExceptionB(String.valueOf(e));
            } else if (e.charAt(0) == 'C') {
                throw new ExceptionC(String.valueOf(e));
            } else if (e.charAt(0) == 'D') {
                throw new ExceptionD(String.valueOf(e));
            } else if (e.charAt(0) == 'E') {
                throw new ExceptionE(String.valueOf(e));
            } else if (e.charAt(0) == 'X') {
                throw new ExceptionX(String.valueOf(e));
            }
            return "get";
        }
    }

    @Test
    public void test() {
        // Format of first param: [exception thrown]-[exception mappers which will return true in isMappable]
        _test("A-A", "A");
        _test("A-AB", "A");
        _test("A-ABC", "A");
        _test("A-ABCD", "A");
        _test("A-ABCDE", "A");
        _test("A-ABCDEX", "A");
        _test("A-C", null);
        _test("A-D", null);
        _test("A-E", null);
        _test("A-D", null);
        _test("A-BCDEX", null);
        _test("A-00000", null);
        _test("A-X", null);


        _test("B-A", "A");
        _test("B-B", "B");
        _test("B-AB", "B");
        _test("B-ABC", "B");
        _test("B-ABCD", "B");
        _test("B-ABCDE", "B");
        _test("B-ABCDEX", "B");
        _test("B-C", null);
        _test("B-D", null);
        _test("B-E", null);
        _test("B-CDEX", null);
        _test("B-X", null);
        _test("B-000", null);

        // C is not an ExtendedExceptionMapper but just ExceptionMapper (always mappable)
        _test("C-C", "C");
        _test("C-A", "C");
        _test("C-AB", "C");
        _test("C-ABC", "C");
        _test("C-AEX", "C");
        _test("C-00000", "C");
        _test("C-ABCDEX", "C");
        _test("C-E", "C");
        _test("C-DE", "C");
        _test("C-D", "C");
        _test("C-X", "C");

        _test("D-000", "C");
        _test("D-A", "C");
        _test("D-B", "C");
        _test("D-C", "C");
        _test("D-D", "D");
        _test("D-E", "C");
        _test("D-ABC", "C");
        _test("D-ABCEX", "C");
        _test("D-ABCDEX", "D");
        _test("D-DE", "D");
        _test("D-ABEX", "C");
        _test("D-AEX", "C");
        _test("D-X", "C");

        _test("E-A", "C");
        _test("E-B", "C");
        _test("E-C", "C");
        _test("E-D", "D");
        _test("E-E", "E");
        _test("E-ABC", "C");
        _test("E-ABCD", "D");
        _test("E-ABCDE", "E");
        _test("E-ABCDEX", "E");
        _test("E-DE", "E");
        _test("E-X", "C");
        _test("E-000000", "C");

        _test("X-A", null);
        _test("X-ABCDE", null);
        _test("X-ABCDEX", "X");
        _test("X-X", "X");

        // Check logs. (??)
        for (final LogRecord logRecord : getLoggedRecords()) {
            for (final String message : new String[]{
                    LocalizationMessages.ERROR_EXCEPTION_MAPPING_ORIGINAL_EXCEPTION(),
                    LocalizationMessages.ERROR_EXCEPTION_MAPPING_THROWN_TO_CONTAINER()}) {

                if (logRecord.getMessage().contains(message) && logRecord.getLevel().intValue() > Level.FINE.intValue()) {
                    fail("Log message should be logged at lower (FINE) level: " + message);
                }
            }
        }
    }

    private void _test(String input, String expectedMapper) {
        final Response response = target("resource").request().post(Entity.entity(input, MediaType.TEXT_PLAIN_TYPE));
        if (expectedMapper == null) {
            Assert.assertEquals(500, response.getStatus());
        } else {
            Assert.assertEquals(200, response.getStatus());
            Assert.assertEquals(expectedMapper, response.readEntity(String.class));
        }
    }


}
