package com.airlock.iam.oauth2.application.configuration.resource;

import com.airlock.iam.oauth2.application.configuration.OAuth2AccessTokenAuthenticationConfig;
import com.airlock.iam.oauth2.application.configuration.OAuth2BearerAccessTokenConfig;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.PluginListProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.Environment;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.ConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.valuereader.PluginListPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.StringPropertyValueReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Plugin(
   description = "<p>OAuth 2.0 / OpenID Connect resource endpoint configuration.</p>\n<p>Authentication against those endpoints must be done using an Access Token\nprovided in a Bearer Token header; for example: <code>Authorization: Bearer pQ8x4f8PXcp9aS84</code></p>\n<p>The endpoint will be available under\n<tt>/&lt;loginapp-uri&gt;/rest/oauth2/authorization-servers/<i>&lt;as-identifier&gt;</i>/resources/<i>&lt;resource-name&gt;</i></tt></p>",
   displayName = "OAuth 2.0 Resource Endpoint"
)
@PluginLicenseRestriction(moduleAffiliation = "OAuthServer")
public class OAuth2ResourceEndpointConfig {
   @PluginListProperty(description = "A list of available resources.", optional = false, minElements = 1)
   private List<OAuth2ResourceConfig> resources;
   private static final String RESOURCES_PROPERTY = "resources";
   @PluginProperty(
      description = "Configures how the Access Token is extracted from the request.",
      defaultValue = OAuth2BearerAccessTokenConfig.class,
      optional = true
   )
   private OAuth2AccessTokenAuthenticationConfig accessTokenAuthenticationConfig;

   @ConfigurationValidator
   public static void validateUniqueIds(
      PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment
   ) throws ConfigurationException {
      Set<String> paths = new HashSet<>();

      for (PluginConfiguration res : PluginListPropertyValueReader.effectivePluginListPropertyValue(config, "resources", env, context)) {
         String path = StringPropertyValueReader.effectiveStringConfigValue(res, "resourceName", env, context);
         if (path != null && !paths.add(path)) {
            throw new PropertyConstraintViolationException("resourceName", res, env, context, "Resource path is not unique.");
         }
      }
   }

   public List<OAuth2ResourceConfig> getResources() {
      return this.resources;
   }

   public OAuth2AccessTokenAuthenticationConfig getAccessTokenAuthenticationConfig() {
      return this.accessTokenAuthenticationConfig;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static OAuth2ResourceEndpointConfig.OAuth2ResourceEndpointConfigBuilder builder() {
      return new OAuth2ResourceEndpointConfig.OAuth2ResourceEndpointConfigBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public OAuth2ResourceEndpointConfig() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public OAuth2ResourceEndpointConfig(List<OAuth2ResourceConfig> resources, OAuth2AccessTokenAuthenticationConfig accessTokenAuthenticationConfig) {
      this.resources = resources;
      this.accessTokenAuthenticationConfig = accessTokenAuthenticationConfig;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2ResourceEndpointConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private List<OAuth2ResourceConfig> resources;
      @SuppressFBWarnings(justification = "generated code")
      private OAuth2AccessTokenAuthenticationConfig accessTokenAuthenticationConfig;

      @SuppressFBWarnings(justification = "generated code")
      OAuth2ResourceEndpointConfigBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourceEndpointConfig.OAuth2ResourceEndpointConfigBuilder resources(List<OAuth2ResourceConfig> resources) {
         this.resources = resources;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourceEndpointConfig.OAuth2ResourceEndpointConfigBuilder accessTokenAuthenticationConfig(
         OAuth2AccessTokenAuthenticationConfig accessTokenAuthenticationConfig
      ) {
         this.accessTokenAuthenticationConfig = accessTokenAuthenticationConfig;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourceEndpointConfig build() {
         return new OAuth2ResourceEndpointConfig(this.resources, this.accessTokenAuthenticationConfig);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2ResourceEndpointConfig.OAuth2ResourceEndpointConfigBuilder(resources="
            + this.resources
            + ", accessTokenAuthenticationConfig="
            + this.accessTokenAuthenticationConfig
            + ")";
      }
   }
}
