package org.jboss.resteasy.plugins.server.vertx;

import io.vertx.core.Context;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import org.apache.commons.codec.binary.Base64;
import org.jboss.resteasy.core.SynchronousDispatcher;
import org.jboss.resteasy.core.ThreadLocalResteasyProviderFactory;
import org.jboss.resteasy.plugins.server.embedded.SecurityDomain;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.util.HttpHeaderNames;
import org.jboss.resteasy.util.HttpResponseCodes;

import javax.ws.rs.core.SecurityContext;
import java.io.IOException;
import java.security.Principal;
import java.util.List;

/**
 * Helper/delegate class to unify Servlet and Filter dispatcher implementations
 *
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @author Norman Maurer
 * @version $Revision: 1 $
 */
public class RequestDispatcher
{
   protected final SynchronousDispatcher dispatcher;
   protected final ResteasyProviderFactory providerFactory;
   protected final SecurityDomain domain;

   public RequestDispatcher(final SynchronousDispatcher dispatcher, final ResteasyProviderFactory providerFactory, final SecurityDomain domain)
   {
      this.dispatcher = dispatcher;
      this.providerFactory = providerFactory;
      this.domain = domain;
   }

   public SynchronousDispatcher getDispatcher()
   {
      return dispatcher;
   }

   public SecurityDomain getDomain()
   {
      return domain;
   }

   public ResteasyProviderFactory getProviderFactory()
   {
      return providerFactory;
   }

   public void service(Context context,
                       HttpServerRequest req,
                       HttpServerResponse resp,
                       HttpRequest vertxReq, HttpResponse vertxResp, boolean handleNotFound) throws IOException
   {

      try
      {
         ResteasyProviderFactory defaultInstance = ResteasyProviderFactory.getInstance();
         if (defaultInstance instanceof ThreadLocalResteasyProviderFactory)
         {
            ThreadLocalResteasyProviderFactory.push(providerFactory);
         }

         SecurityContext securityContext;
         if (domain != null)
         {
            securityContext = basicAuthentication(vertxReq, vertxResp);
            if (securityContext == null) // not authenticated
            {
               return;
            }
         } else
         {
            securityContext = new VertxSecurityContext();
         }
         try
         {

            ResteasyProviderFactory.pushContext(SecurityContext.class, securityContext);
            ResteasyProviderFactory.pushContext(Context.class, context);
            ResteasyProviderFactory.pushContext(HttpServerRequest.class, req);
            ResteasyProviderFactory.pushContext(HttpServerResponse.class, resp);
            ResteasyProviderFactory.pushContext(Vertx.class, context.owner());
            if (handleNotFound)
            {
               dispatcher.invoke(vertxReq, vertxResp);
            } else
            {
               dispatcher.invokePropagateNotFound(vertxReq, vertxResp);
            }
         } finally
         {
            ResteasyProviderFactory.clearContextData();
         }
      } finally
      {
         ResteasyProviderFactory defaultInstance = ResteasyProviderFactory.getInstance();
         if (defaultInstance instanceof ThreadLocalResteasyProviderFactory)
         {
            ThreadLocalResteasyProviderFactory.pop();
         }

      }
   }

   private SecurityContext basicAuthentication(HttpRequest request, HttpResponse response) throws IOException
   {
      List<String> headers = request.getHttpHeaders().getRequestHeader(HttpHeaderNames.AUTHORIZATION);
      if (!headers.isEmpty())
      {
         String auth = headers.get(0);
         if (auth.length() > 5)
         {
            String type = auth.substring(0, 5);
            type = type.toLowerCase();
            if ("basic".equals(type))
            {
               String cookie = auth.substring(6);
               cookie = new String(Base64.decodeBase64(cookie.getBytes()));
               String[] split = cookie.split(":");
               Principal user = null;
               try
               {
                  user = domain.authenticate(split[0], split[1]);
                  return new VertxSecurityContext(user, domain, "BASIC", true);
               } catch (SecurityException e)
               {
                  response.sendError(HttpResponseCodes.SC_UNAUTHORIZED);
                  return null;
               }
            } else
            {
               response.sendError(HttpResponseCodes.SC_UNAUTHORIZED);
               return null;
            }
         }
      }
      return null;
   }


}
