package org.jboss.resteasy.test.client.exception;

import static org.junit.jupiter.api.Assertions.fail;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import jakarta.ws.rs.ProcessingException;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.core.GenericType;
import jakarta.ws.rs.core.Response;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit5.ArquillianExtension;
import org.jboss.logging.Logger;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.client.jaxrs.internal.ClientInvocation;
import org.jboss.resteasy.test.client.exception.resource.ExceptionBufferingResource;
import org.jboss.resteasy.utils.PortProviderUtil;
import org.jboss.resteasy.utils.TestUtil;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

/**
 * @tpSubChapter Resteasy-client
 * @tpChapter Client tests
 * @tpSince RESTEasy 3.0.16
 * @tpTestCaseDetails Regression test for RESTEASY-981
 */
@ExtendWith(ArquillianExtension.class)
@RunAsClient
public class ExceptionBufferingTest {

    protected static final Logger logger = Logger.getLogger(ExceptionBufferingTest.class.getName());

    private static final String DEPLOYMENT_TRUE = "buffer";
    private static final String DEPLOYMENT_FALSE = "nobuffer";
    private static final String DEPLOYMENT_DEFAULT = "default";

    protected static ResteasyClient client;

    @Deployment(name = DEPLOYMENT_TRUE)
    public static Archive<?> deployTrue() {
        WebArchive war = TestUtil.prepareArchive(DEPLOYMENT_TRUE);
        Map<String, String> params = new HashMap<>();
        params.put("resteasy.buffer.exception.entity", "true");
        return TestUtil.finishContainerPrepare(war, params, ExceptionBufferingResource.class);
    }

    @Deployment(name = DEPLOYMENT_FALSE)
    public static Archive<?> deployFalse() {
        WebArchive war = TestUtil.prepareArchive(DEPLOYMENT_FALSE);
        Map<String, String> params = new HashMap<>();
        params.put("resteasy.buffer.exception.entity", "false");
        return TestUtil.finishContainerPrepare(war, params, ExceptionBufferingResource.class);
    }

    @Deployment(name = DEPLOYMENT_DEFAULT)
    public static Archive<?> deployDefault() {
        WebArchive war = TestUtil.prepareArchive(DEPLOYMENT_DEFAULT);
        client = (ResteasyClient) ClientBuilder.newClient();
        return TestUtil.finishContainerPrepare(war, null, ExceptionBufferingResource.class);
    }

    @AfterAll
    public static void init() throws Exception {
        client.close();
    }

    /**
     * @tpTestDetails Test default value of resteasy.buffer.exception.entity property
     * @tpSince RESTEasy 3.0.16
     */
    @Test
    public void testBufferedResponseDefault() throws Exception {
        Response response = null;

        try {
            ResteasyWebTarget target = client.target(PortProviderUtil.generateURL("/test", DEPLOYMENT_DEFAULT));
            Invocation invocation = target.request().buildGet();
            response = invocation.invoke();
            logger.info("status: " + response.getStatus());
            String s = ClientInvocation.extractResult(new GenericType<String>(String.class), response, null);
            fail("Was expecting an exception: " + s);
        } catch (Exception e) {
            logger.info("caught: " + e);
            String entity = response.readEntity(String.class);
            logger.info("exception entity: " + entity);
            Assertions.assertEquals("test", entity, "Wrong response content");
        }
    }

    /**
     * @tpTestDetails Test false value of resteasy.buffer.exception.entity property
     * @tpSince RESTEasy 3.0.16
     */
    @Test
    public void testBufferedResponseFalse() throws Exception {
        Response response = null;

        try {
            ResteasyWebTarget target = client.target(PortProviderUtil.generateURL("/test", DEPLOYMENT_FALSE));
            Invocation invocation = target.request().buildGet();
            response = invocation.invoke();
            logger.info("status: " + response.getStatus());
            String s = ClientInvocation.extractResult(new GenericType<String>(String.class), response, null);
            fail("Was expecting an exception: " + s);
        } catch (Exception e) {
            logger.info("caught: " + e);
            try {
                String s = response.readEntity(String.class);
                fail("Was expecting a second exception: " + s);
            } catch (ProcessingException e1) {
                logger.info("and caught: " + e1);
                Assertions.assertTrue(e1.getCause() instanceof IOException, "Wrong exception thrown");
                Assertions.assertEquals("Attempted read on closed stream.", e1.getCause().getMessage());
            } catch (Exception e1) {
                fail("Was expecting a ProcessingException instead of " + e1);
            }
        }
    }

    /**
     * @tpTestDetails Test true value of resteasy.buffer.exception.entity property
     * @tpSince RESTEasy 3.0.16
     */
    @Test
    public void testBufferedResponseTrue() throws Exception {
        Response response = null;

        try {
            ResteasyWebTarget target = client.target(PortProviderUtil.generateURL("/test", DEPLOYMENT_TRUE));
            Invocation invocation = target.request().buildGet();
            response = invocation.invoke();
            logger.info("status: " + response.getStatus());
            String s = ClientInvocation.extractResult(new GenericType<String>(String.class), response, null);
            fail("Was expecting an exception: " + s);
        } catch (Exception e) {
            logger.info("caught: " + e);
            String entity = response.readEntity(String.class);
            logger.info("exception entity: " + entity);
            Assertions.assertEquals("test", entity, "Wrong responce content");
        }
    }
}
