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

import com.airlock.iam.oauth2.application.configuration.client.OAuth2RemoteUsernameResource;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Optional;

public final class OAuth2ResourcesConfigValidator {
   private final String containedResourcesField;
   private final String resourceRequestsField;
   private final PluginConfiguration config;
   private final ConfigurationEnvironment env;
   private final ConfigurationContext context;
   private final Optional<String> idTokenResourcesField;

   private OAuth2ResourcesConfigValidator(
      String containedResourcesField,
      String resourceRequestsField,
      PluginConfiguration config,
      ConfigurationEnvironment env,
      ConfigurationContext context,
      @Nullable String idTokenResourcesField
   ) {
      this.containedResourcesField = containedResourcesField;
      this.resourceRequestsField = resourceRequestsField;
      this.config = config;
      this.env = env;
      this.context = context;
      this.idTokenResourcesField = Optional.ofNullable(idTokenResourcesField);
   }

   public void checkExactlyOneUsernameResourceRequest() throws ConfigurationException {
      if (this.countResourceRequestNameResources() != 1) {
         throw new PropertyConstraintViolationException(
            this.resourceRequestsField,
            this.config,
            this.env,
            this.context,
            "There must be exactly one remote username resource configured in the \"Resource Requests\" list, otherwise the username is ambiguous or unknown."
         );
      }
   }

   public void checkExactlyOneUsernameResourceRequestOrIdTokenResource() throws ConfigurationException {
      int counter = this.countResourceRequestNameResources() + this.countIdTokenUsernameResources();
      if (counter != 1) {
         throw new PropertyConstraintViolationException(
            this.resourceRequestsField,
            this.config,
            this.env,
            this.context,
            "There must be exactly one remote username resource configured in the \"Resource Requests\"  and the \"ID Token Resources\" lists, otherwise the username is ambiguous or unknown."
         );
      }
   }

   private int countResourceRequestNameResources() throws ConfigurationException {
      int counter = 0;

      for (PluginConfiguration resourceRequest : PluginListPropertyValueReader.effectivePluginListPropertyValue(
         this.config, this.resourceRequestsField, this.env, this.context
      )) {
         List<PluginConfiguration> containedResources = PluginListPropertyValueReader.effectivePluginListPropertyValue(
            resourceRequest, this.containedResourcesField, this.env, this.context
         );
         counter += countUsernameResources(containedResources);
      }

      return counter;
   }

   private static int countUsernameResources(List<PluginConfiguration> resources) throws ConfigurationException {
      if (resources == null) {
         return 0;
      }

      int counter = 0;

      for (PluginConfiguration pluginConfiguration : resources) {
         if (pluginConfiguration != null && pluginConfiguration.isOfPluginClass(OAuth2RemoteUsernameResource.class)) {
            counter++;
         }
      }

      return counter;
   }

   private int countIdTokenUsernameResources() throws ConfigurationException {
      List<PluginConfiguration> idTokenResources = this.config
         .getPluginListPropertyConfigurationValue(this.idTokenResourcesField.orElseThrow(), this.env, this.context);
      return countUsernameResources(idTokenResources);
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2ResourcesConfigValidatorBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String containedResourcesField;
      @SuppressFBWarnings(justification = "generated code")
      private String resourceRequestsField;
      @SuppressFBWarnings(justification = "generated code")
      private PluginConfiguration config;
      @SuppressFBWarnings(justification = "generated code")
      private ConfigurationEnvironment env;
      @SuppressFBWarnings(justification = "generated code")
      private ConfigurationContext context;
      @SuppressFBWarnings(justification = "generated code")
      private String idTokenResourcesField;

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

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourcesConfigValidator.OAuth2ResourcesConfigValidatorBuilder containedResourcesField(String containedResourcesField) {
         this.containedResourcesField = containedResourcesField;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourcesConfigValidator.OAuth2ResourcesConfigValidatorBuilder resourceRequestsField(String resourceRequestsField) {
         this.resourceRequestsField = resourceRequestsField;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourcesConfigValidator.OAuth2ResourcesConfigValidatorBuilder config(PluginConfiguration config) {
         this.config = config;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourcesConfigValidator.OAuth2ResourcesConfigValidatorBuilder env(ConfigurationEnvironment env) {
         this.env = env;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourcesConfigValidator.OAuth2ResourcesConfigValidatorBuilder context(ConfigurationContext context) {
         this.context = context;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourcesConfigValidator.OAuth2ResourcesConfigValidatorBuilder idTokenResourcesField(@Nullable String idTokenResourcesField) {
         this.idTokenResourcesField = idTokenResourcesField;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourcesConfigValidator build() {
         return new OAuth2ResourcesConfigValidator(
            this.containedResourcesField, this.resourceRequestsField, this.config, this.env, this.context, this.idTokenResourcesField
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2ResourcesConfigValidator.OAuth2ResourcesConfigValidatorBuilder(containedResourcesField="
            + this.containedResourcesField
            + ", resourceRequestsField="
            + this.resourceRequestsField
            + ", config="
            + this.config
            + ", env="
            + this.env
            + ", context="
            + this.context
            + ", idTokenResourcesField="
            + this.idTokenResourcesField
            + ")";
      }
   }
}
