package org.jboss.resteasy.test.asynch.resource;

import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import jakarta.ws.rs.container.AsyncResponse;
import jakarta.ws.rs.container.CompletionCallback;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.Response;

import org.jboss.logging.Logger;
import org.jboss.resteasy.core.ResteasyContext;
import org.jboss.resteasy.core.interception.jaxrs.SuspendableContainerRequestContext;
import org.jboss.resteasy.spi.HttpRequest;

public abstract class AsyncRequestFilter implements ContainerRequestFilter {

   private final String name;
   private volatile String callbackException;
   private static final Logger LOG = Logger.getLogger(AsyncRequestFilter.class);

   public AsyncRequestFilter(final String name)
   {
      this.name = name;
   }

   @Override
   public void filter(ContainerRequestContext requestContext) throws IOException
   {
      requestContext.getHeaders().add("RequestFilterCallback"+name, String.valueOf(callbackException));
      callbackException = null;

      SuspendableContainerRequestContext ctx = (SuspendableContainerRequestContext) requestContext;
      String action = ctx.getHeaderString(name);
      LOG.error("Filter request for "+name+" with action: "+action);
      if("sync-pass".equals(action)) {
         // do nothing
      }else if("sync-fail".equals(action)) {
         ctx.abortWith(Response.ok(name).build());
      }else if("async-pass".equals(action)) {
         ctx.suspend();
         ExecutorService executor = Executors.newSingleThreadExecutor();
         executor.submit(() -> ctx.resume());
      }else if("async-pass-instant".equals(action)) {
         ctx.suspend();
         ctx.resume();
      }else if("async-fail".equals(action)) {
         ctx.suspend();
         ExecutorService executor = Executors.newSingleThreadExecutor();
         executor.submit(() -> ctx.abortWith(Response.ok(name).build()));
      }else if("async-fail-instant".equals(action)) {
         ctx.suspend();
         ctx.abortWith(Response.ok(name).build());
      }else if("async-throw-late".equals(action)) {
         ctx.suspend();
         HttpRequest req = ResteasyContext.getContextData(HttpRequest.class);
         ExecutorService executor = Executors.newSingleThreadExecutor();
         executor.submit(() -> {
            try
            {
               Thread.sleep(2000);
            } catch (InterruptedException e)
            {
               // TODO Auto-generated catch block
               LOG.error("Error:", e);
            }
            AsyncResponse resp = req.getAsyncContext().getAsyncResponse();
            resp.register((CompletionCallback) (t) -> {
               if(callbackException != null)
                  throw new RuntimeException("Callback called twice");
               callbackException = Objects.toString(t);
            });
            if("true".equals(req.getHttpHeaders().getHeaderString("UseExceptionMapper")))
               ctx.resume(new AsyncFilterException("ouch"));
            else
               ctx.resume(new Throwable("ouch"));
         });
      }
      LOG.error("Filter request for "+name+" with action: "+action+" done");
   }

}
