package com.airlock.iam.login.app.misc.configuration;

import com.airlock.iam.login.application.configuration.targetapp.RoleTimeoutRuleConfig;
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.StringProperty;
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 = "Defines user-specific idle-timeout and role life-time for a role.")
public class UserSpecificRoleTimeoutDefinition {
   @StringProperty(description = "The name of the role to enhance with timeouts.", optional = false, exampleValues = "admin")
   private String role;
   @StringProperty(
      description = "The context data field holding a user-specific timeout identifier to be matched against the Role-Timeout Rules.",
      optional = false,
      exampleValues = "adminTimeouts"
   )
   private String contextDataProperty;
   @PluginListProperty(
      description = "Mapping between timeout identifiers in the context data field and the resulting timeouts to add to the role.",
      optional = true,
      displayName = "Role-Timeout Rules",
      minElements = 1
   )
   private List<RoleTimeoutRuleConfig> roleTimeoutRules;
   @IntegerProperty(
      description = "Default idle timeout in seconds. This value is taken, when the string in the context data doesn't match any of the identifiers in the Role-Timeout Rules.",
      exampleValues = {60, 300, 1800},
      optional = false
   )
   private int defaultIdleTimeout;
   private static final String DEFAULT_IDLE_TIMEOUT_FIELD = "defaultIdleTimeout";
   @IntegerProperty(
      description = "Default role life-time in seconds. This value is taken, when the string in the context data doesn't match any of the identifiers in the Role-Timeout Rules. If this value is not set, no life timeout is added to the role.",
      exampleValues = {600, 1800, 7200},
      optional = true
   )
   private Integer defaultLifeTime;
   private static final String DEFAULT_LIFE_TIME_FIELD = "defaultLifeTime";

   @ConfigurationValidator
   public static void checkProperties(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment) throws ConfigurationException {
      Integer defaultIdleTimeout = IntegerPropertyValueReader.effectiveIntegerConfigValue(config, "defaultIdleTimeout", env, context);
      if (defaultIdleTimeout != null) {
         Integer lifeIdleTimeout = IntegerPropertyValueReader.effectiveIntegerConfigValue(config, "defaultLifeTime", env, context);
         if (lifeIdleTimeout != null) {
            if (lifeIdleTimeout < defaultIdleTimeout) {
               throw new PropertyConstraintViolationException(
                  "defaultLifeTime", config, env, context, "The life timeout should be equal or greater than the idle timeout."
               );
            }
         }
      }
   }

   public String getRole() {
      return this.role;
   }

   public String getContextDataProperty() {
      return this.contextDataProperty;
   }

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

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

   public Integer getDefaultLifeTime() {
      return this.defaultLifeTime;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public UserSpecificRoleTimeoutDefinition(
      String role, String contextDataProperty, List<RoleTimeoutRuleConfig> roleTimeoutRules, int defaultIdleTimeout, Integer defaultLifeTime
   ) {
      this.role = role;
      this.contextDataProperty = contextDataProperty;
      this.roleTimeoutRules = roleTimeoutRules;
      this.defaultIdleTimeout = defaultIdleTimeout;
      this.defaultLifeTime = defaultLifeTime;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class UserSpecificRoleTimeoutDefinitionBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String role;
      @SuppressFBWarnings(justification = "generated code")
      private String contextDataProperty;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<RoleTimeoutRuleConfig> roleTimeoutRules;
      @SuppressFBWarnings(justification = "generated code")
      private int defaultIdleTimeout;
      @SuppressFBWarnings(justification = "generated code")
      private Integer defaultLifeTime;

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

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificRoleTimeoutDefinition.UserSpecificRoleTimeoutDefinitionBuilder role(String role) {
         this.role = role;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificRoleTimeoutDefinition.UserSpecificRoleTimeoutDefinitionBuilder contextDataProperty(String contextDataProperty) {
         this.contextDataProperty = contextDataProperty;
         return this;
      }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificRoleTimeoutDefinition.UserSpecificRoleTimeoutDefinitionBuilder 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 UserSpecificRoleTimeoutDefinition.UserSpecificRoleTimeoutDefinitionBuilder clearRoleTimeoutRules() {
         if (this.roleTimeoutRules != null) {
            this.roleTimeoutRules.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificRoleTimeoutDefinition.UserSpecificRoleTimeoutDefinitionBuilder defaultIdleTimeout(int defaultIdleTimeout) {
         this.defaultIdleTimeout = defaultIdleTimeout;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificRoleTimeoutDefinition.UserSpecificRoleTimeoutDefinitionBuilder defaultLifeTime(Integer defaultLifeTime) {
         this.defaultLifeTime = defaultLifeTime;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserSpecificRoleTimeoutDefinition build() {
         return new UserSpecificRoleTimeoutDefinition(
            this.role, this.contextDataProperty, 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 "UserSpecificRoleTimeoutDefinition.UserSpecificRoleTimeoutDefinitionBuilder(role="
            + this.role
            + ", contextDataProperty="
            + this.contextDataProperty
            + ", roleTimeoutRules="
            + this.roleTimeoutRules
            + ", defaultIdleTimeout="
            + this.defaultIdleTimeout
            + ", defaultLifeTime="
            + this.defaultLifeTime
            + ")";
      }
   }
}
