package com.airlock.iam.login.application.configuration.targetapp;

import com.airlock.iam.common.application.configuration.valueprovider.contextdata.ContextDataStringValueProviderConfig;
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.IntegerProperty;
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.IntegerPropertyValueReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

@Plugin(
   description = "<p>Timeouts are applied according to the \"Role Timeout Rules\". </p><ul><li>If no rule matches, the \"Default Idle Timeout\" and \"Default Lifetime\" timeouts are applied. </li><li>If no rule matches and only one of \"Default Idle Timeout\" and \"Default Lifetime\" is set, the other value is implicitly set to 0, which is the default Gateway timeout. </li><li>If no rule matches and neither \"Default Idle Timeout\" nor \"Default Lifetime\" are set, the role timeouts are used. If none are set, the default Gateway timeouts are applied.</li></ul><p>0 can be used to set the default Gateway timeout value explicitly.</p>"
)
public class UserSpecificTimeoutProviderConfig implements TimeoutProviderConfig {
   @PluginProperty(description = "The Context Data string, which is matched against the rules defined in the \"Role Timeout Rules\".", optional = false)
   private ContextDataStringValueProviderConfig contextDataStringValueProvider;
   @PluginListProperty(
      description = "Rules, which are matched against the Context Data string. If a rule matches, the according \"Idle Timeout\" and \"Lifetime\" are applied. Only the first match is applied. If no rule matches, \"Default Idle Timeout\" and \"Default Lifetime\" are applied.",
      optional = false,
      displayName = "Role Timeout Rules",
      minElements = 1
   )
   private List<RoleTimeoutRuleConfig> roleTimeoutRules;
   public static final String ROLE_TIMEOUT_RULE_DISPLAY_NAME = "Role Timeout Rule";
   public static final String ROLE_TIMEOUT_RULES_DISPLAY_NAME = "Role Timeout Rules";
   @IntegerProperty(
      description = "\"Default Idle Timeout\" in seconds. This value is applied, when no \"Role Timeout Rule\" matches. The idle timeout configured on the Gateway is applied when this value is not set and: <ul><li>the role doesn't have an idle timeout</li><li>the role does have an idle timeout and a lifetime has been set in this plugin</li></ul>",
      exampleValues = {60, 300, 1800},
      minValue = 0,
      displayName = "Default Idle Timeout [s]",
      optional = true
   )
   private Integer defaultIdleTimeout;
   private static final String DEFAULT_IDLE_TIMEOUT_FIELD = "defaultIdleTimeout";
   public static final String DEFAULT_IDLE_TIMEOUT_DISPLAY_NAME = "Default Idle Timeout";
   @IntegerProperty(
      description = "Default Lifetime in seconds. This value is applied, when no \"Role Timeout Rule\" matches. The lifetime configured on the Gateway (WAF) is applied when this value is not set and: <ul><li>the role doesn't have a lifetime</li><li>the role does have a lifetime and an idle timeout has been set in this plugin</li></ul>",
      exampleValues = {600, 1800, 7200},
      minValue = 0,
      displayName = "Default Lifetime [s]",
      optional = true
   )
   private Integer defaultLifetime;
   private static final String DEFAULT_LIFETIME_FIELD = "defaultLifetime";
   public static final String DEFAULT_LIFETIME_DISPLAY_NAME = "Default Lifetime";

   @ConfigurationValidator
   public static void checkProperties(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment) throws ConfigurationException {
      Integer defaultIdleTimeout = IntegerPropertyValueReader.effectiveIntegerConfigValue(config, "defaultIdleTimeout", env, context);
      Integer defaultLifeTimeout = IntegerPropertyValueReader.effectiveIntegerConfigValue(config, "defaultLifetime", env, context);
      if (defaultLifeTimeout != null
         && defaultIdleTimeout != null
         && defaultLifeTimeout != 0
         && defaultIdleTimeout != 0
         && defaultLifeTimeout < defaultIdleTimeout) {
         throw new PropertyConstraintViolationException(
            "defaultLifetime", config, env, context, "The \"Default Lifetime\" should be equal or greater than the \"Default Idle Timeout\"."
         );
      }
   }

   public ContextDataStringValueProviderConfig getContextDataStringValueProvider() {
      return this.contextDataStringValueProvider;
   }

   public List<RoleTimeoutRuleConfig> getRoleTimeoutRules() {
      return this.roleTimeoutRules;
   }

   public Integer getDefaultIdleTimeout() {
      return this.defaultIdleTimeout;
   }

   public Integer getDefaultLifetime() {
      return this.defaultLifetime;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public UserSpecificTimeoutProviderConfig(
      ContextDataStringValueProviderConfig contextDataStringValueProvider,
      List<RoleTimeoutRuleConfig> roleTimeoutRules,
      Integer defaultIdleTimeout,
      Integer defaultLifetime
   ) {
      this.contextDataStringValueProvider = contextDataStringValueProvider;
      this.roleTimeoutRules = roleTimeoutRules;
      this.defaultIdleTimeout = defaultIdleTimeout;
      this.defaultLifetime = defaultLifetime;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class UserSpecificTimeoutProviderConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private ContextDataStringValueProviderConfig contextDataStringValueProvider;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<RoleTimeoutRuleConfig> roleTimeoutRules;
      @SuppressFBWarnings(justification = "generated code")
      private Integer defaultIdleTimeout;
      @SuppressFBWarnings(justification = "generated code")
      private Integer defaultLifetime;

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

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificTimeoutProviderConfig.UserSpecificTimeoutProviderConfigBuilder contextDataStringValueProvider(
         ContextDataStringValueProviderConfig contextDataStringValueProvider
      ) {
         this.contextDataStringValueProvider = contextDataStringValueProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificTimeoutProviderConfig.UserSpecificTimeoutProviderConfigBuilder roleTimeoutRule(RoleTimeoutRuleConfig roleTimeoutRule) {
         if (this.roleTimeoutRules == null) {
            this.roleTimeoutRules = new ArrayList<>();
         }

         this.roleTimeoutRules.add(roleTimeoutRule);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificTimeoutProviderConfig.UserSpecificTimeoutProviderConfigBuilder roleTimeoutRules(
         Collection<? extends RoleTimeoutRuleConfig> roleTimeoutRules
      ) {
         if (roleTimeoutRules == null) {
            throw new NullPointerException("roleTimeoutRules cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificTimeoutProviderConfig.UserSpecificTimeoutProviderConfigBuilder clearRoleTimeoutRules() {
         if (this.roleTimeoutRules != null) {
            this.roleTimeoutRules.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificTimeoutProviderConfig.UserSpecificTimeoutProviderConfigBuilder defaultIdleTimeout(Integer defaultIdleTimeout) {
         this.defaultIdleTimeout = defaultIdleTimeout;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificTimeoutProviderConfig.UserSpecificTimeoutProviderConfigBuilder defaultLifetime(Integer defaultLifetime) {
         this.defaultLifetime = defaultLifetime;
         return this;
      }

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

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "UserSpecificTimeoutProviderConfig.UserSpecificTimeoutProviderConfigBuilder(contextDataStringValueProvider="
            + this.contextDataStringValueProvider
            + ", roleTimeoutRules="
            + this.roleTimeoutRules
            + ", defaultIdleTimeout="
            + this.defaultIdleTimeout
            + ", defaultLifetime="
            + this.defaultLifetime
            + ")";
      }
   }
}
