package com.airlock.iam.authentication.application.configuration.idpropagation;

import com.airlock.iam.core.misc.sso.RestIdentityPropagator;
import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
import com.airlock.iam.flow.api.application.configuration.valueprovider.ValueMapProviderConfig;
import com.airlock.iam.flow.application.configuration.selection.condition.AlwaysTrueConditionConfig;
import com.airlock.iam.flow.shared.application.configuration.valueprovider.RoleProviderConfig;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginPropertyLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.PluginListProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
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.Optional;
import org.apache.commons.lang3.StringUtils;

@Plugin(
   description = "<p>Adapter for a legacy Identity Propagator plugin.</p><p>The set of identity propagators that can be configured is limited to those that don't redirect the response and don't require the HTTP request.</p><p>The roles made available to the identity propagator are those obtained by the Role Provider plugins configured here. Thus, if no Role Providers are configured, the identity propagator will not get any roles, not even the user's roles from the DB.</p>",
   displayName = "Legacy ID Propagation Adapter"
)
public class LegacyIdPropagatorConfig implements IdentityPropagationConfig {
   @PluginProperty(description = "The identity propagator plugin to be used.", optional = false)
   private RestIdentityPropagator identityPropagator;
   @PluginProperty(
      description = "Defines the condition under which the users identity is propagated using the configured identity propagation.",
      defaultValue = AlwaysTrueConditionConfig.class,
      optional = true
   )
   private FlowConditionConfig condition;
   @PluginListProperty(
      description = "Plugins to determine the roles to propagate. These are only application-specific roles, not the Airlock Gateway (WAF) roles. If nothing is configured, no roles can be propagated.",
      optional = true
   )
   private List<RoleProviderConfig> roleProviders;
   @PluginListProperty(
      description = "Plugins to determine an alternative username to propagate. This username is used as user ID for this identity propagation. The providers are asked to provide an alternative username in configured order. After the first username is supplied, subsequent providers are no longer invoked. If nothing configured, or no provider supplies a username, the user ID of the authenticated user is propagated.<p>Note that when possible these transformations should be configured in the target application instead. However, if username transformations are configured both here and in the target application, then the transformations defined in the target application will be ignored and the transformations defined here will be applied directly to the technical user ID.</p>",
      optional = true
   )
   @PluginPropertyLicenseRestriction(moduleAffiliation = "SubIdentities")
   private List<UsernameProviderConfig> usernameProviders;
   @PluginListProperty(
      description = "Values provided by these value map providers are added to the persistent context-data of the authentee that is created for the identity propagator. They can then be referred to like normal context-data. Providers may overwrite each others values. The order of the list defines the runtime order and therefore influence the resulting data set.",
      displayName = "Context-Data Providers",
      optional = true
   )
   private List<ValueMapProviderConfig> contextDataProviders;
   @StringProperty(
      description = "<p>The optional key by which the password should be retrieved from password steps. </p><p>If no key is configured or no password was entered for this key, no password is propagated.</p>",
      optional = true,
      suggestedValues = "PASSWORD"
   )
   private String passwordAttributeKey;

   public Optional<String> getPasswordAttributeName() {
      return Optional.ofNullable(StringUtils.trimToNull(this.passwordAttributeKey));
   }

   public RestIdentityPropagator getIdentityPropagator() {
      return this.identityPropagator;
   }

   public FlowConditionConfig getCondition() {
      return this.condition;
   }

   public List<RoleProviderConfig> getRoleProviders() {
      return this.roleProviders;
   }

   public List<UsernameProviderConfig> getUsernameProviders() {
      return this.usernameProviders;
   }

   public List<ValueMapProviderConfig> getContextDataProviders() {
      return this.contextDataProviders;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public LegacyIdPropagatorConfig(
      RestIdentityPropagator identityPropagator,
      FlowConditionConfig condition,
      List<RoleProviderConfig> roleProviders,
      List<UsernameProviderConfig> usernameProviders,
      List<ValueMapProviderConfig> contextDataProviders,
      String passwordAttributeKey
   ) {
      this.identityPropagator = identityPropagator;
      this.condition = condition;
      this.roleProviders = roleProviders;
      this.usernameProviders = usernameProviders;
      this.contextDataProviders = contextDataProviders;
      this.passwordAttributeKey = passwordAttributeKey;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class LegacyIdPropagatorConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private RestIdentityPropagator identityPropagator;
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig condition;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<RoleProviderConfig> roleProviders;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<UsernameProviderConfig> usernameProviders;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<ValueMapProviderConfig> contextDataProviders;
      @SuppressFBWarnings(justification = "generated code")
      private String passwordAttributeKey;

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

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder identityPropagator(RestIdentityPropagator identityPropagator) {
         this.identityPropagator = identityPropagator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder condition(FlowConditionConfig condition) {
         this.condition = condition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder roleProvider(RoleProviderConfig roleProvider) {
         if (this.roleProviders == null) {
            this.roleProviders = new ArrayList<>();
         }

         this.roleProviders.add(roleProvider);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder roleProviders(Collection<? extends RoleProviderConfig> roleProviders) {
         if (roleProviders == null) {
            throw new NullPointerException("roleProviders cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder clearRoleProviders() {
         if (this.roleProviders != null) {
            this.roleProviders.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder usernameProvider(UsernameProviderConfig usernameProvider) {
         if (this.usernameProviders == null) {
            this.usernameProviders = new ArrayList<>();
         }

         this.usernameProviders.add(usernameProvider);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder usernameProviders(Collection<? extends UsernameProviderConfig> usernameProviders) {
         if (usernameProviders == null) {
            throw new NullPointerException("usernameProviders cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder clearUsernameProviders() {
         if (this.usernameProviders != null) {
            this.usernameProviders.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder contextDataProvider(ValueMapProviderConfig contextDataProvider) {
         if (this.contextDataProviders == null) {
            this.contextDataProviders = new ArrayList<>();
         }

         this.contextDataProviders.add(contextDataProvider);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder contextDataProviders(Collection<? extends ValueMapProviderConfig> contextDataProviders) {
         if (contextDataProviders == null) {
            throw new NullPointerException("contextDataProviders cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder clearContextDataProviders() {
         if (this.contextDataProviders != null) {
            this.contextDataProviders.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder passwordAttributeKey(String passwordAttributeKey) {
         this.passwordAttributeKey = passwordAttributeKey;
         return this;
      }

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

         List usernameProviders = switch (this.usernameProviders == null ? 0 : this.usernameProviders.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.usernameProviders.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.usernameProviders));
         };

         return new LegacyIdPropagatorConfig(
            this.identityPropagator,
            this.condition,
            roleProviders,
            usernameProviders,
            switch (this.contextDataProviders == null ? 0 : this.contextDataProviders.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.contextDataProviders.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.contextDataProviders));
            },
            this.passwordAttributeKey
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "LegacyIdPropagatorConfig.LegacyIdPropagatorConfigBuilder(identityPropagator="
            + this.identityPropagator
            + ", condition="
            + this.condition
            + ", roleProviders="
            + this.roleProviders
            + ", usernameProviders="
            + this.usernameProviders
            + ", contextDataProviders="
            + this.contextDataProviders
            + ", passwordAttributeKey="
            + this.passwordAttributeKey
            + ")";
      }
   }
}
