package com.airlock.iam.login.application.configuration.location.interpret;

import com.airlock.iam.common.api.application.configuration.location.transform.StringTransformerConfig;
import com.airlock.iam.login.api.application.configuration.location.extract.URIValueExtractorConfig;
import com.airlock.iam.login.api.application.configuration.location.interpret.LocationInterpreterURITransformerConfig;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.PluginListProperty;
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;

@Plugin(
   description = "Highly customizable plugin to transform the given URI and potentially extract a value by applying the following procedure:<br><ol><li>The optional \"URI Transformers\" are called in the configured order to transform the URI to a new URI. The output of a preceding transformer is used as input of a subsequent transformer. If an URI transformer returns a veto, the default value configured in this plugin is used as the resulting value.<br>Those plugins are used to perform generic transformations on the URI like regular expression replacements or to use a query parameter as the new URI.</li><li>The optional \"Value Extractors\" are now called in the configured order to extract a string value from the potentially transformed URI. The first extractor able to return a non-empty value stops the chain. If no extractor returns a non-empty value, the whole URI is passed on unchanged.<br>Those extractors are used to extract a query parameter or a path segment containing the target value.</li><li>Finally the optional \"String Transformers\" are called in the configured order to transform the final string. The output of the preceding transformer is used as input of the subsequent transformer. If a string transformer returns a veto, the default value configured in this plugin is used as overall value.<br>They can for example be used to normalize values, for example convert the string to lowercase or convert values from one format to another (e.g. convert 'GER' to 'de').</li></ol>"
)
public final class AdvancedLocationInterpreterConfig implements LocationInterpreterConfig {
   @StringProperty(description = "The default value to be returned if a transformer produces a veto.", optional = true)
   private String defaultValue;
   @PluginListProperty(
      description = "The chain of URI transformers that transform the original URI.",
      displayName = "URI Transformers",
      mandatoryUnless = "valueExtractors",
      optional = true,
      minElements = 1
   )
   private List<LocationInterpreterURITransformerConfig> uriTransformers;
   private static final String URI_TRANSFORMERS_FIELD = "uriTransformers";
   @PluginListProperty(
      description = "The extractors that extract string values from the transformed URI. The result of the first extractor returning a non-empty value is used.",
      optional = true,
      mandatoryUnless = "uriTransformers",
      minElements = 1
   )
   private List<URIValueExtractorConfig> valueExtractors;
   private static final String VALUE_EXTRACTORS_FIELD = "valueExtractors";
   @PluginListProperty(
      description = "The chain of string transformers that transform the extracted string value to the final interpretation result.",
      optional = true
   )
   private List<StringTransformerConfig> stringTransformers;

   @Override
   public Optional<String> getDefaultValue() {
      return Optional.ofNullable(this.defaultValue);
   }

   public List<LocationInterpreterURITransformerConfig> getURITransformers() {
      return this.uriTransformers;
   }

   public List<URIValueExtractorConfig> getValueExtractors() {
      return this.valueExtractors;
   }

   public List<StringTransformerConfig> getStringTransformers() {
      return this.stringTransformers;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public AdvancedLocationInterpreterConfig(
      String defaultValue,
      List<LocationInterpreterURITransformerConfig> uriTransformers,
      List<URIValueExtractorConfig> valueExtractors,
      List<StringTransformerConfig> stringTransformers
   ) {
      this.defaultValue = defaultValue;
      this.uriTransformers = uriTransformers;
      this.valueExtractors = valueExtractors;
      this.stringTransformers = stringTransformers;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class AdvancedLocationInterpreterConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String defaultValue;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<LocationInterpreterURITransformerConfig> uriTransformers;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<URIValueExtractorConfig> valueExtractors;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<StringTransformerConfig> stringTransformers;

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

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder defaultValue(String defaultValue) {
         this.defaultValue = defaultValue;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder uriTransformer(LocationInterpreterURITransformerConfig uriTransformer) {
         if (this.uriTransformers == null) {
            this.uriTransformers = new ArrayList<>();
         }

         this.uriTransformers.add(uriTransformer);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder uriTransformers(
         Collection<? extends LocationInterpreterURITransformerConfig> uriTransformers
      ) {
         if (uriTransformers == null) {
            throw new NullPointerException("uriTransformers cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder clearUriTransformers() {
         if (this.uriTransformers != null) {
            this.uriTransformers.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder valueExtractor(URIValueExtractorConfig valueExtractor) {
         if (this.valueExtractors == null) {
            this.valueExtractors = new ArrayList<>();
         }

         this.valueExtractors.add(valueExtractor);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder valueExtractors(
         Collection<? extends URIValueExtractorConfig> valueExtractors
      ) {
         if (valueExtractors == null) {
            throw new NullPointerException("valueExtractors cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder clearValueExtractors() {
         if (this.valueExtractors != null) {
            this.valueExtractors.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder stringTransformer(StringTransformerConfig stringTransformer) {
         if (this.stringTransformers == null) {
            this.stringTransformers = new ArrayList<>();
         }

         this.stringTransformers.add(stringTransformer);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder stringTransformers(
         Collection<? extends StringTransformerConfig> stringTransformers
      ) {
         if (stringTransformers == null) {
            throw new NullPointerException("stringTransformers cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder clearStringTransformers() {
         if (this.stringTransformers != null) {
            this.stringTransformers.clear();
         }

         return this;
      }

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

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

         return new AdvancedLocationInterpreterConfig(
            this.defaultValue, uriTransformers, valueExtractors, switch (this.stringTransformers == null ? 0 : this.stringTransformers.size()) {
               case 0 -> Collections.emptyList();
               case 1 -> Collections.singletonList(this.stringTransformers.get(0));
               default -> Collections.unmodifiableList(new ArrayList<>(this.stringTransformers));
            }
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "AdvancedLocationInterpreterConfig.AdvancedLocationInterpreterConfigBuilder(defaultValue="
            + this.defaultValue
            + ", uriTransformers="
            + this.uriTransformers
            + ", valueExtractors="
            + this.valueExtractors
            + ", stringTransformers="
            + this.stringTransformers
            + ")";
      }
   }
}
