package org.whispersystems.dropwizard.simpleauth;

import org.glassfish.jersey.server.model.AnnotatedMethod;

import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.FeatureContext;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Optional;

import io.dropwizard.auth.Auth;

public class AuthDynamicFeature implements DynamicFeature {

  private AuthFilter[] authFilters;

  public AuthDynamicFeature(AuthFilter... authFilters) {
    this.authFilters = authFilters;
  }

  @Override
  public void configure(ResourceInfo resourceInfo, FeatureContext context) {
    AnnotatedMethod annotatedMethod       = new AnnotatedMethod(resourceInfo.getResourceMethod());
    Annotation[][]  parameterAnnotations  = annotatedMethod.getParameterAnnotations();
    Class<?>[]      parameterTypes        = annotatedMethod.getParameterTypes      ();
    Type[]          parameterGenericTypes = annotatedMethod.getGenericParameterTypes();

    verifyAuthAnnotations(parameterAnnotations);

    for (int i=0;i<parameterAnnotations.length;i++) {
      for (Annotation annotation : parameterAnnotations[i]) {
        if (annotation instanceof Auth) {
          Type parameterType = parameterTypes[i];

          if (parameterType == Optional.class) {
            parameterType = ((ParameterizedType)parameterGenericTypes[i]).getActualTypeArguments()[0];
            context.register(new WebApplicationExceptionCatchingFilter(getFilterFor(parameterType)));
          } else {
            context.register(getFilterFor(parameterType));
          }
        }
      }
    }
  }

  private AuthFilter getFilterFor(Type parameterType) {
    for (AuthFilter filter : authFilters) {
      if (filter.supports(parameterType)) return filter;
    }

    throw new IllegalArgumentException("No authenticator for type: " + parameterType);
  }

  private void verifyAuthAnnotations(Annotation[][] parameterAnnotations) {
    int authCount = 0;

    for (Annotation[] annotations : parameterAnnotations) {
      for (Annotation annotation : annotations) {
        if (annotation instanceof Auth) authCount++;
      }
    }

    if (authCount > 1) {
      throw new IllegalArgumentException("Only one @Auth tag supported per resource method!");
    }
  }

}