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

import com.airlock.iam.common.application.configuration.user.UserProfileValueProviderConfig;
import com.airlock.iam.common.domain.model.user.profileitems.StringProfileItemLogic;
import com.airlock.iam.core.misc.persistency.UserIterator;
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.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.RegexProperty;
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.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.exception.PropertyTypeMismatchException;
import com.airlock.iam.plugin.api.valuereader.BooleanPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.PluginPropertyValueReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.regex.Pattern;

@Plugin(
   description = "Plugin to hold a configurable user profile item of type string. This will be represented as a text input field and its value is added to the user's context data, provided that the property name matches the property name in the configured user data. A regular expression can be given to check the input, e.g. to ensure that it has the format of an email address.",
   displayName = "String User Profile Item"
)
public class StringUserProfileItemConfig extends UserProfileItemConfigBase {
   public static final String STRING_USER_PROFILE_ITEM_DISPLAY_NAME = "String User Profile Item";
   @RegexProperty(
      description = "<p>Pattern for validating the value of the field.</p><p>The provided regex is used in Java for server-side validation and potentially in Javascript for client-side validation. The capabilities of these regex interpreters differ. Therefore make sure to only use patterns that are equivalent in both types of interpreters.</p>",
      optional = true,
      exampleValues = "[A-Za-z0-9_\\-.]+@[A-Za-z0-9\\-.]+\\.[A-Za-z]+"
   )
   private Pattern validationPattern;
   @PluginProperty(
      description = "<p>If defined, the user persister is used to check whether the value is unique by querying the corresponding user iterator plugin.</p><p>This user iterator must provide the context data value specified by this profile item.<br/>Usually, the same plugin is used that was used to load the user data to the form this profile item is part of.</p>",
      optional = true
   )
   private UserIterator checkUniqueness;
   @BooleanProperty(
      description = "<p>If set to <code>true</code>, uniqueness is also checked against the <code>username</code>. The value entered by the user is not allowed to exist neither in the configured property nor as a username. This is mainly used in conjunction with a username transformer, where login is possible with an alias property in addition to the username.</p><p>This flag is only checked if <code>checkUniqueness</code> is configured.</p>",
      optional = true,
      defaultValue = "false"
   )
   private boolean checkUniquenessAgainstUsername;
   @PluginProperty(
      description = "If configured, the profile item is prefilled with the provided value. This feature can be used to suggest administrators on possible values or to prefill a common value when creating a user via Adminapp UI. This property only has an effect when creating a user. Furthermore it is only allowed for mandatory items because only mandatory items are displayed in the user create dialog.",
      optional = true
   )
   private UserProfileValueProviderConfig prefill;
   private static final String PREFILL_PROPERTY_NAME = "prefill";

   public StringUserProfileItemConfig(
      String stringResourceKey,
      String propertyName,
      boolean optional,
      boolean modifiable,
      boolean validateOnlyChangedValues,
      boolean sortable,
      Pattern validationPattern,
      UserIterator checkUniqueness,
      boolean checkUniquenessAgainstUsername,
      UserProfileValueProviderConfig prefill
   ) {
      super(stringResourceKey, propertyName, optional, modifiable, validateOnlyChangedValues, sortable);
      this.validationPattern = validationPattern;
      this.checkUniqueness = checkUniqueness;
      this.checkUniquenessAgainstUsername = checkUniquenessAgainstUsername;
      this.prefill = prefill;
   }

   @ConfigurationValidator
   static void ensurePrefillForMandatoryOnly(
      PluginConfiguration pc, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment
   ) throws PropertyConstraintViolationException, PropertyTypeMismatchException {
      boolean optional = BooleanPropertyValueReader.effectiveBooleanPropertyValue(pc, "optional", env, context);
      PluginConfiguration prefill = PluginPropertyValueReader.effectivePluginPropertyValue(pc, "prefill", env, context);
      if (optional && prefill != null) {
         throw new PropertyConstraintViolationException("prefill", pc, env, context, "\"Prefill\" can only be configured on mandatory items.");
      }
   }

   protected Pattern getValidationPattern() {
      return this.validationPattern;
   }

   @Nullable
   public UserIterator getCheckUniqueness() {
      return this.checkUniqueness;
   }

   public boolean isCheckUniquenessAgainstUsername() {
      return this.checkUniquenessAgainstUsername;
   }

   @Nullable
   public UserProfileValueProviderConfig getPrefill() {
      return this.prefill;
   }

   public StringProfileItemLogic getProfileItemLogic() {
      return StringProfileItemLogic.builder()
         .checkUniquenessAgainstUsername(this.checkUniquenessAgainstUsername)
         .optional(this.isOptional())
         .modifiable(this.isModifiable())
         .propertyName(this.getPropertyName())
         .uniquenessChecker(this.checkUniqueness)
         .validateOnlyChangedValues(this.isValidateOnlyChangedValues())
         .validationPattern(this.validationPattern)
         .emailAddress(false)
         .build();
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class StringUserProfileItemConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String stringResourceKey;
      @SuppressFBWarnings(justification = "generated code")
      private String propertyName;
      @SuppressFBWarnings(justification = "generated code")
      private boolean optional;
      @SuppressFBWarnings(justification = "generated code")
      private boolean modifiable;
      @SuppressFBWarnings(justification = "generated code")
      private boolean validateOnlyChangedValues;
      @SuppressFBWarnings(justification = "generated code")
      private boolean sortable;
      @SuppressFBWarnings(justification = "generated code")
      private Pattern validationPattern;
      @SuppressFBWarnings(justification = "generated code")
      private UserIterator checkUniqueness;
      @SuppressFBWarnings(justification = "generated code")
      private boolean checkUniquenessAgainstUsername;
      @SuppressFBWarnings(justification = "generated code")
      private UserProfileValueProviderConfig prefill;

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

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder stringResourceKey(String stringResourceKey) {
         this.stringResourceKey = stringResourceKey;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder propertyName(String propertyName) {
         this.propertyName = propertyName;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder optional(boolean optional) {
         this.optional = optional;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder modifiable(boolean modifiable) {
         this.modifiable = modifiable;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder validateOnlyChangedValues(boolean validateOnlyChangedValues) {
         this.validateOnlyChangedValues = validateOnlyChangedValues;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder sortable(boolean sortable) {
         this.sortable = sortable;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder validationPattern(Pattern validationPattern) {
         this.validationPattern = validationPattern;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder checkUniqueness(UserIterator checkUniqueness) {
         this.checkUniqueness = checkUniqueness;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder checkUniquenessAgainstUsername(boolean checkUniquenessAgainstUsername) {
         this.checkUniquenessAgainstUsername = checkUniquenessAgainstUsername;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig.StringUserProfileItemConfigBuilder prefill(UserProfileValueProviderConfig prefill) {
         this.prefill = prefill;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public StringUserProfileItemConfig build() {
         return new StringUserProfileItemConfig(
            this.stringResourceKey,
            this.propertyName,
            this.optional,
            this.modifiable,
            this.validateOnlyChangedValues,
            this.sortable,
            this.validationPattern,
            this.checkUniqueness,
            this.checkUniquenessAgainstUsername,
            this.prefill
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "StringUserProfileItemConfig.StringUserProfileItemConfigBuilder(stringResourceKey="
            + this.stringResourceKey
            + ", propertyName="
            + this.propertyName
            + ", optional="
            + this.optional
            + ", modifiable="
            + this.modifiable
            + ", validateOnlyChangedValues="
            + this.validateOnlyChangedValues
            + ", sortable="
            + this.sortable
            + ", validationPattern="
            + this.validationPattern
            + ", checkUniqueness="
            + this.checkUniqueness
            + ", checkUniquenessAgainstUsername="
            + this.checkUniquenessAgainstUsername
            + ", prefill="
            + this.prefill
            + ")";
      }
   }
}
