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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.common.api.domain.model.contextdata.ContextData;
import com.airlock.iam.common.api.domain.model.contextdata.ContextDataItem;
import com.airlock.iam.common.api.domain.model.user.User;
import com.airlock.iam.core.application.configuration.contextdata.ContextDataItemNameConfig;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.RegexProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;
import java.util.regex.Pattern;

@Plugin(description = "Condition that compares a string context data value against a regular expression.")
public final class StringContextDataUserGroupConditionConfig extends AbstractContextDataUserGroupConditionConfig {
   private static final ELogger LOG = ELoggerFactory.getLogger(StringContextDataUserGroupConditionConfig.class);
   @RegexProperty(
      description = "Regular expression pattern matched against the (default string representation of the) context data value. If it matches, the user is considered to be member of the group.",
      exampleValues = {"CERT", "MTAN"},
      optional = false
   )
   private Pattern pattern;
   public static final String PATTERN_CONFIG_NAME = "pattern";

   public StringContextDataUserGroupConditionConfig(String groupName, ContextDataItemNameConfig contextPropertyName, Pattern pattern) {
      super(groupName, contextPropertyName);
      this.pattern = pattern;
   }

   @Override
   public boolean isUserMemberOfGroup(User user) {
      ContextData contextData = user.getContextData();
      Optional<ContextDataItem<Object>> objectContextDataItem = contextData.find(this.getContextDataItemName().getContextDataName());
      if (objectContextDataItem.isEmpty()) {
         LOG.debug(
            String.format(
               "User context data field '%s' is configured in group condition with name '%s', but no data available on the user. This could be either that the user has no data, or a configuration error; in the latter case make sure to configure the field in the used user persister.",
               this.getContextDataItemName().getContextDataName(),
               this.getName()
            )
         );
         return false;
      } else {
         String contextDataValue = this.getStringValue(objectContextDataItem.get());
         return this.pattern.matcher(contextDataValue.trim()).matches();
      }
   }

   private String getStringValue(ContextDataItem<Object> contextDataItem) {
      Object value = contextDataItem.getValue();
      if (!String.class.isAssignableFrom(value.getClass())) {
         throw new FailureException(
            String.format("Configured context data item '%s' does not reference a string data field. Please reconfigure.", contextDataItem.getKey())
         );
      } else {
         return (String)value;
      }
   }

   public static StringContextDataUserGroupConditionConfig.StringContextDataUserGroupConditionConfigBuilder builder() {
      return new StringContextDataUserGroupConditionConfig.StringContextDataUserGroupConditionConfigBuilder();
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public StringContextDataUserGroupConditionConfig(Pattern pattern) {
      this.pattern = pattern;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class StringContextDataUserGroupConditionConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String groupName;
      @SuppressFBWarnings(justification = "generated code")
      private ContextDataItemNameConfig contextPropertyName;
      @SuppressFBWarnings(justification = "generated code")
      private Pattern pattern;

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

      @SuppressFBWarnings(justification = "generated code")
      public StringContextDataUserGroupConditionConfig.StringContextDataUserGroupConditionConfigBuilder groupName(String groupName) {
         this.groupName = groupName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringContextDataUserGroupConditionConfig.StringContextDataUserGroupConditionConfigBuilder contextPropertyName(
         ContextDataItemNameConfig contextPropertyName
      ) {
         this.contextPropertyName = contextPropertyName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringContextDataUserGroupConditionConfig.StringContextDataUserGroupConditionConfigBuilder pattern(Pattern pattern) {
         this.pattern = pattern;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringContextDataUserGroupConditionConfig build() {
         return new StringContextDataUserGroupConditionConfig(this.groupName, this.contextPropertyName, this.pattern);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "StringContextDataUserGroupConditionConfig.StringContextDataUserGroupConditionConfigBuilder(groupName="
            + this.groupName
            + ", contextPropertyName="
            + this.contextPropertyName
            + ", pattern="
            + this.pattern
            + ")";
      }
   }
}
