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

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Response;

import org.jboss.resteasy.client.exception.ResteasyWebApplicationException;
import org.jboss.resteasy.client.exception.WebApplicationExceptionWrapper;
import org.jboss.resteasy.test.client.exception.ClientWebApplicationExceptionTest;
import org.jboss.resteasy.utils.PortProviderUtil;
import org.junit.Assert;

@Path("test")
public class ClientWebApplicationExceptionResource {

   private static Client client = ClientBuilder.newClient();

   private static WebTarget oldBehaviorTarget = client.target(PortProviderUtil.generateURL(
           "/app/test/", ClientWebApplicationExceptionTest.oldBehaviorDeploymentName)
   );
   private static WebTarget newBehaviorTarget = client.target(PortProviderUtil.generateURL(
           "/app/test/", ClientWebApplicationExceptionTest.newBehaviorDeploymentName)
   );

   /**
    * Throws an instance of WebApplicationException from oldExceptions table. The Response returned by
    * WebApplicationException.getResponse() will be used by the container to create an HTTP response.
    *
    * @param i determines element of oldExceptions to be thrown
    * @throws Exception
    */
   @GET
   @Path("exception/old/{i}")
   public String oldException(@PathParam("i") int i) throws Exception {
      throw ClientWebApplicationExceptionTest.oldExceptions[i];
   }

   /**
    * Throws an instance of ResteasyWebApplicationException from newExceptions table.
    * ResteasyWebApplicationException.getResponse() will be used by the container to create a sanitized
    * HTTP response.
    *
    * @param i determines element of newExceptions to be thrown
    * @throws Exception
    */
   @GET
   @Path("exception/new/{i}")
   public String newException(@PathParam("i") int i) throws Exception {
      throw ClientWebApplicationExceptionTest.newExceptions[i];
   }

   /**
    * Uses a Client to call oldException() to get an HTTP response derived from a WebApplicationException.
    * Client will throw a WebApplicationException because
    * ResteasyContextParameters.RESTEASY_ORIGINAL_WEBAPPLICATIONEXCEPTION_BEHAVIOR is true.
    *
    * @param i determines element of oldExceptions to be thrown by oldException()
    * @throws Exception
    */
   @GET
   @Path("nocatch/old/old/{i}")
   public String noCatchOldOld(@PathParam("i") int i) throws Exception {
      return oldBehaviorTarget.path("exception/old/" + i).request().get(String.class);
   }

   /**
    * Uses a Client to call oldException() to get an HTTP response derived from a WebApplicationException.
    * Client will throw a ResteasyWebApplicationException because
    * ResteasyContextParameters.RESTEASY_ORIGINAL_WEBAPPLICATIONEXCEPTION_BEHAVIOR is false.
    *
    * @param i determines element of oldExceptions to be thrown by oldException()
    * @throws Exception
    */
   @GET
   @Path("nocatch/new/old/{i}")
   public String noCatchNewOld(@PathParam("i") int i) throws Exception {
      return newBehaviorTarget.path("exception/old/" + i).request().get(String.class);
   }

   /**
    * Uses a Client to call newException() to get an HTTP response derived from a ResteasyWebApplicationException.
    * Client will throw a WebApplicationException because
    * ResteasyContextParameters.RESTEASY_ORIGINAL_WEBAPPLICATIONEXCEPTION_BEHAVIOR is true.
    *
    * @param i determines element of newExceptions to be thrown by newException()
    * @throws Exception
    */
   @GET
   @Path("nocatch/old/new/{i}")
   public String noCatchOldNew(@PathParam("i") int i) throws Exception {
      return oldBehaviorTarget.path("exception/new/" + i).request().get(String.class);
   }

   /**
    * Uses a Client to call newException() to get an HTTP response derived from a ResteasyWebApplicationException.
    * Client will throw a ResteasyWebApplicationException because
    * ResteasyContextParameters.RESTEASY_ORIGINAL_WEBAPPLICATIONEXCEPTION_BEHAVIOR is false.
    *
    * @param i determines element of newExceptions to be thrown by newException()
    * @throws Exception
    */
   @GET
   @Path("nocatch/new/new/{i}")
   public String noCatchNewNew(@PathParam("i") int i) throws Exception {
      return newBehaviorTarget.path("exception/new/" + i).request().get(String.class);
   }

   /**
    * It is assumed that ResteasyContextParameters.RESTEASY_ORIGINAL_WEBAPPLICATIONEXCEPTION_BEHAVIOR is
    * set to "true" before this method is invoked.
    *
    * Uses a Client to call oldException(). Since the old behavior is configured, the Client will throw a
    * WebApplicationException, which is caught and examined. It should match the WebApplicationException
    * thrown by oldException(). That WebApplicationException is then rethrown.
    *
    * @param i determines element of oldExceptions to be thrown by oldException()
    * @throws Exception
    */
   @GET
   @Path("catch/old/old/{i}")
   public String catchOldOld(@PathParam("i") int i) throws Exception {
      try {
         oldBehaviorTarget.path("exception/old/" + i).request().get(String.class);
         throw new Exception("expected exception");
      } catch (ResteasyWebApplicationException e) {
         throw new Exception("didn't expect ResteasyWebApplicationException");
      } catch (WebApplicationException e) {
         Response response = e.getResponse();
         Assert.assertEquals(ClientWebApplicationExceptionTest.oldExceptions[i].getResponse().getStatus(), response.getStatus());
         Assert.assertEquals(ClientWebApplicationExceptionTest.oldExceptions[i].getResponse().getHeaderString("foo"), response.getHeaderString("foo"));
         Assert.assertEquals(ClientWebApplicationExceptionTest.oldExceptions[i].getResponse().getEntity(), response.readEntity(String.class));
         Assert.assertEquals(ClientWebApplicationExceptionTest.oldExceptionMap.get(response.getStatus()), e.getClass());
         throw e;
      } catch (Exception e) {
         throw new Exception("expected ResteasyWebApplicationException, not " + e.getClass());
      }
   }

   /**
    * It is assumed that ResteasyContextParameters.RESTEASY_ORIGINAL_WEBAPPLICATIONEXCEPTION_BEHAVIOR is
    * set to "true" before this method is invoked.
    *
    * Uses a Client to call newException(). Since the old behavior is configured, the Client will throw a
    * WebApplicationException, which is caught and examined for a sanitized Response and matching status.
    *
    * @param i determines element of newExceptions to be thrown by newException()
    * @throws Exception
    */
   @GET
   @Path("catch/old/new/{i}")
   public String catchOldNew(@PathParam("i") int i) throws Exception {
      try {
         oldBehaviorTarget.path("exception/new/" + i).request().get(String.class);
         throw new Exception("expected exception");
      } catch (ResteasyWebApplicationException e) {
         throw new Exception("didn't expect ResteasyWebApplicationException");
      } catch (WebApplicationException e) {
         Response response = e.getResponse();
         Assert.assertEquals(ClientWebApplicationExceptionTest.newExceptions[i].getResponse().getStatus(), response.getStatus());
         Assert.assertNull(response.getHeaderString("foo"));
         Assert.assertTrue(response.readEntity(String.class).length() == 0);
         Assert.assertEquals(ClientWebApplicationExceptionTest.oldExceptionMap.get(response.getStatus()), e.getClass());
         throw e;
      } catch (Exception e) {
         throw new Exception("expected ResteasyWebApplicationException, not " + e.getClass());
      }
   }

   /**
    * It is assumed that ResteasyContextParameters.RESTEASY_ORIGINAL_WEBAPPLICATIONEXCEPTION_BEHAVIOR holds
    * "false" when this method is invoked.
    *
    * Uses a Client to call oldException().  Since the new behavior is configured, the client will throw a
    * WebApplicationExceptionWrapper, which is caught and examined. getResponse() and the unwrapped Response
    * should match the WebApplicationException thrown by oldException().
    * That WebApplicationExceptionWrapper is then rethrown.
    *
    * @param i determines element of oldExceptions to be thrown by oldException()
    * @throws Exception
    */
   @GET
   @Path("catch/new/old/{i}")
   public String catchNewOld(@PathParam("i") int i) throws Exception {
      try {
         newBehaviorTarget.path("exception/old/" + i).request().get(String.class);
         throw new Exception("expected exception");
      } catch (WebApplicationException e) {
         Response notSanitizedResponse = e.getResponse();
         Assert.assertNotNull(notSanitizedResponse);
         Assert.assertEquals(WebApplicationExceptionWrapper.unwrap(e).getResponse(), notSanitizedResponse);
         Assert.assertEquals(ClientWebApplicationExceptionTest.oldExceptions[i].getResponse().getStatus(), notSanitizedResponse.getStatus());
         Assert.assertEquals(ClientWebApplicationExceptionTest.oldExceptions[i].getResponse().getHeaderString("foo"), notSanitizedResponse.getHeaderString("foo"));
         Assert.assertEquals(ClientWebApplicationExceptionTest.oldExceptions[i].getResponse().getEntity(), notSanitizedResponse.readEntity(String.class));
         Assert.assertEquals(ClientWebApplicationExceptionTest.newExceptionMap.get(notSanitizedResponse.getStatus()), e.getClass());
         throw e;
      } catch (Exception e) {
         throw new Exception("expected WebApplicationException, not " + e.getClass());
      }
   }

   /**
    * It is assumed that ResteasyContextParameters.RESTEASY_ORIGINAL_WEBAPPLICATIONEXCEPTION_BEHAVIOR holds
    * "false" when this method is invoked.
    *
    * Uses a Client to call newException(). Since the new behavior is configured, the client will throw a
    * WebApplicationExceptionWrapper, which is caught and examined. getResponse() and the unwrapped Response
    * should a sanitized response.
    * That WebApplicationExceptionWrapper is then rethrown.
    *
    * @param i determines element of newExceptions to be thrown by newException()
    * @throws Exception
    */
   @GET
   @Path("catch/new/new/{i}")
   public String catchNewNew(@PathParam("i") int i) throws Exception {
      try {
         newBehaviorTarget.path("exception/new/" + i).request().get(String.class);
         throw new Exception("expected exception");
      } catch (WebApplicationException e) {
         Response sanitizedResponse = e.getResponse();
         Assert.assertNotNull(sanitizedResponse);
         Assert.assertEquals(WebApplicationExceptionWrapper.unwrap(e).getResponse(), sanitizedResponse);
         Assert.assertEquals(ClientWebApplicationExceptionTest.newExceptions[i].getResponse().getStatus(), sanitizedResponse.getStatus());
         Assert.assertNull(sanitizedResponse.getHeaderString("foo"));
         Assert.assertTrue(sanitizedResponse.readEntity(String.class).isEmpty());
         Assert.assertEquals(ClientWebApplicationExceptionTest.newExceptionMap.get(sanitizedResponse.getStatus()), e.getClass());
         throw e;
      } catch (Exception e) {
         throw new Exception("expected WebApplicationException, not " + e.getClass());
      }
   }
}
