package org.jboss.resteasy.security.doseta;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import jakarta.annotation.Priority;
import jakarta.ws.rs.ConstrainedTo;
import jakarta.ws.rs.CookieParam;
import jakarta.ws.rs.FormParam;
import jakarta.ws.rs.HeaderParam;
import jakarta.ws.rs.MatrixParam;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.RuntimeType;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.container.DynamicFeature;
import jakarta.ws.rs.container.ResourceInfo;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.FeatureContext;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.ext.Provider;

import org.jboss.resteasy.annotations.security.doseta.Verifications;
import org.jboss.resteasy.annotations.security.doseta.Verify;
import org.jboss.resteasy.core.ResteasyContext;
import org.jboss.resteasy.security.doseta.i18n.Messages;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
@Provider
@ConstrainedTo(RuntimeType.SERVER)
public class ServerDigitalVerificationHeaderDecoratorFeature implements DynamicFeature
{
   @Override
   public void configure(ResourceInfo resourceInfo, FeatureContext configurable)
   {
      Verify verify = resourceInfo.getResourceMethod().getAnnotation(Verify.class);
      Verifications verifications = resourceInfo.getResourceClass().getAnnotation(Verifications.class);

      resourceInfo.getResourceMethod();
      if (verify != null || verifications != null)
      {
         configurable.register(new DigitalVerificationHeaderDecorator(verify, verifications, hasEntityParameter(resourceInfo.getResourceMethod())));
      }

   }

   @Priority(Priorities.HEADER_DECORATOR)
   public static class DigitalVerificationHeaderDecorator extends AbstractDigitalVerificationHeaderDecorator implements ContainerRequestFilter
   {
      protected boolean hasEntityParameter;

      public DigitalVerificationHeaderDecorator(final Verify verify, final Verifications verifications, final boolean hasEntityParameter)
      {
         this.verify = verify;
         this.verifications = verifications;
         this.hasEntityParameter = hasEntityParameter;
      }

      @Override
      public void filter(ContainerRequestContext requestContext) throws IOException
      {
         if (hasEntityParameter)
         {
            requestContext.setProperty(Verifier.class.getName(), create());
         }
         else
         {
            verify(requestContext, create());
         }
      }

   }

   static boolean hasEntityParameter(Method method)
   {
      Annotation[][] annotations = method.getParameterAnnotations();
      for (int i = 0; i < annotations.length; i++)
      {
         boolean match = false;
         for (int j = 0; j < annotations[i].length; j++)
         {
            if (annotations[i][j].annotationType().equals(MatrixParam.class)
               || annotations[i][j].annotationType().equals(QueryParam.class)
               || annotations[i][j].annotationType().equals(PathParam.class)
               || annotations[i][j].annotationType().equals(CookieParam.class)
               || annotations[i][j].annotationType().equals(HeaderParam.class)
               || annotations[i][j].annotationType().equals(Context.class)
               || annotations[i][j].annotationType().equals(FormParam.class))
            {
               match = true;
               break;
            }
         }
         if (!match)
         {
            return true;
         }
      }
      return false;
   }

   protected static void verify(ContainerRequestContext context, Verifier verifier)
   {
      MultivaluedMap<String, String> headers = context.getHeaders();
      List<String> strings = headers.get(DKIMSignature.DKIM_SIGNATURE);
      if (strings == null)
      {
         throw new UnauthorizedSignatureException(Messages.MESSAGES.thereWasNoSignatureHeader(DKIMSignature.DKIM_SIGNATURE));
      }

      List<DKIMSignature> signatures = new ArrayList<DKIMSignature>();
      for (String headerVal : strings)
      {
         try
         {
            signatures.add(new DKIMSignature(headerVal));
         }
         catch (Exception e)
         {
            throw new UnauthorizedSignatureException(Messages.MESSAGES.malformedSignatureHeader(DKIMSignature.DKIM_SIGNATURE));
         }
      }

      if (verifier.getRepository() == null)
      {
         KeyRepository repository = (KeyRepository) context.getProperty(KeyRepository.class.getName());
         if (repository == null)
         {
            repository = ResteasyContext.getContextData(KeyRepository.class);
         }
         verifier.setRepository(repository);
      }

      VerificationResults results = verifier.verify(signatures, headers, null);
      if (results.isVerified() == false)
      {
         throw new UnauthorizedSignatureException(results);
      }
   }
}
