package com.airlock.iam.admin.application.configuration.tokens;

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.property.PluginListProperty;
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.PropertyConfigurationException;
import com.airlock.iam.plugin.api.valuereader.PluginListPropertyValueReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import java.util.stream.Collectors;

@Plugin(description = "Configuration of tokens, i.e. management of tokens.")
public class TokensConfiguration {
   @PluginListProperty(description = "Token managers for the Adminapp.", optional = false, minElements = 1)
   private List<TokenManager> tokenManagers;
   public static final String TOKEN_MANAGERS_PROPERTY = "tokenManagers";

   @ConfigurationValidator
   static void validateManagers(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment) throws PropertyConfigurationException {
      List<PluginConfiguration> tokenManagers = PluginListPropertyValueReader.effectivePluginListPropertyValue(config, "tokenManagers", env, context);
      Set<String> duplicateManagers = tokenManagers.stream()
         .<String>map(PluginConfiguration::getPluginClassDisplayName)
         .collect(Collectors.groupingBy(c -> (String)c, Collectors.counting()))
         .entrySet()
         .stream()
         .filter(p -> p.getValue() > 1L)
         .map(Entry::getKey)
         .collect(Collectors.toSet());
      if (!duplicateManagers.isEmpty()) {
         throw new PropertyConfigurationException(
            config,
            env,
            context,
            "It is not allowed to have more than one Token Manager of the same plugin type. Found multiple managers of type: "
               + String.join(", ", duplicateManagers),
            new String[]{"tokenManagers"}
         );
      }
   }

   public List<TokenManager> getTokenManagers() {
      return this.tokenManagers;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public TokensConfiguration(List<TokenManager> tokenManagers) {
      this.tokenManagers = tokenManagers;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class TokensConfigurationBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<TokenManager> tokenManagers;

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

      @SuppressFBWarnings(justification = "generated code")
      public TokensConfiguration.TokensConfigurationBuilder tokenManager(TokenManager tokenManager) {
         if (this.tokenManagers == null) {
            this.tokenManagers = new ArrayList<>();
         }

         this.tokenManagers.add(tokenManager);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TokensConfiguration.TokensConfigurationBuilder tokenManagers(Collection<? extends TokenManager> tokenManagers) {
         if (tokenManagers == null) {
            throw new NullPointerException("tokenManagers cannot be null");
         }

         if (this.tokenManagers == null) {
            this.tokenManagers = new ArrayList<>();
         }

         this.tokenManagers.addAll(tokenManagers);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TokensConfiguration.TokensConfigurationBuilder clearTokenManagers() {
         if (this.tokenManagers != null) {
            this.tokenManagers.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public TokensConfiguration build() {
         return new TokensConfiguration(switch (this.tokenManagers == null ? 0 : this.tokenManagers.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.tokenManagers.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.tokenManagers));
         });
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "TokensConfiguration.TokensConfigurationBuilder(tokenManagers=" + this.tokenManagers + ")";
      }
   }
}
