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

import com.airlock.iam.common.application.configuration.event.LoginappSubscribedEventConfig;
import com.airlock.iam.common.domain.model.event.RemoteEventSubscriberSettings;
import com.airlock.iam.core.misc.util.httpclient.HttpClientConfig;
import com.airlock.iam.flow.api.application.configuration.valueprovider.ValueMapProviderConfig;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.EnumProperty;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
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 = "An event subscriber that executes an HTTP request to forward the event data to a remote endpoint.",
   displayName = "Remote Event Subscriber (Loginapp)"
)
public final class LoginappRemoteEventSubscriberConfig extends LoginappGeneralEventSubscriberConfig {
   @StringProperty(
      description = "URL to which the request will be sent. The URL can contain variables (using the <tt>${variableName}</tt> notation) that will be replaced by the values provided by the event and the configured value providers. See the \"Body Template\" property for a list of all available variables. All values are URL-encoded before being inserted into the URL. To ensure a successful connection the URL should include a scheme - usually either 'http' or 'https'.",
      displayName = "Remote URL",
      exampleValues = "https://my.remote.host/api/${event.data.userId}/notify",
      optional = false
   )
   private String url;
   @StringProperty(
      description = "Template for the request body. The following syntax can be used to include data in the template. <ul><li><tt>${valueMapKey}</tt> for the value of <tt>valueMapKey</tt> provided by any of the configured Value Map Providers.</li><li><tt>${event.createdAt,date,format}</tt> to include the date/time at which the event was created, where \"format\" is a date pattern like \"yyyy-MM-dd HH:mm:ss\".</li></ul>Depending on the event and on the conditions in which the event originated, the following variables may also be available: <ul><li><tt>event.createdAt</tt></li><li><tt>event.data.airlock2FAAccountId</tt></li><li><tt>event.data.airlock2FADeviceId</tt></li><li><tt>event.data.activeAuthenticationMethod</tt></li><li><tt>event.data.authenticationMethods</tt></li><li><tt>event.data.browser</tt></li><li><tt>event.data.city</tt></li><li><tt>event.data.contextDataChanged.%s.newValue</tt> (where \"%s\" is replaced by the context-data field name).</li><li><tt>event.data.contextDataChanged.%s.oldValue</tt> (where \"%s\" is replaced by the context-data field name).</li><li><tt>event.data.countryCode</tt></li><li><tt>event.data.crontoDeviceId</tt></li><li><tt>event.data.device</tt></li><li><tt>event.data.deviceTokenId</tt></li><li><tt>event.data.fidoCredentialId</tt></li><li><tt>event.data.fidoPublicKeyCredentialId</tt></li><li><tt>event.data.oauth2.authorizationServerId</tt></li><li><tt>event.data.fidoRelyingPartyId</tt></li><li><tt>event.data.lockReason</tt></li><li><tt>event.data.mtanNewPhoneNumber</tt></li><li><tt>event.data.mtanNumberId</tt></li><li><tt>event.data.mtanOldPhoneNumber</tt></li><li><tt>event.data.newEmailAddress</tt></li><li><tt>event.data.oauth2.acceptedScopes</tt></li><li><tt>event.data.oauth2.alwaysGrantedScopes</tt></li><li><tt>event.data.oauth2.clientId</tt></li><li><tt>event.data.oauth2.deniedScopes</tt></li><li><tt>event.data.oldEmailAddress</tt></li><li><tt>event.data.operatingSystem</tt></li><li><tt>event.data.previousAuthenticationMethod</tt></li><li><tt>event.data.stepResult.attributes.&lt;attribute-name&gt;</tt> (where &lt;attribute-name&gt ist the name of the additional attribute, could be nested.)</li><li><tt>event.data.stepResult.errorCode</tt></li><li><tt>event.data.stepResult.nextAction</tt></li><li><tt>event.data.stepResult.type</tt></li><li><tt>event.data.userId</tt></li><li><tt>event.id</tt></li><li><tt>event.metadata.requestIp</tt></li><li><tt>event.metadata.userAgent</tt></li><li><tt>event.source.applicationId</tt></li><li><tt>event.source.configurationContext</tt></li><li><tt>event.source.flowId</tt></li><li><tt>event.source.stepId</tt></li></ul>Variables that are not defined are replaced by an empty string. The Airlock IAM documentation provides further information about the availability of specific variables. All values are encoded/escaped before being inserted into the request body.",
      isMultiLineText = true,
      optional = true,
      exampleValues = "{\"type\": \"user-locked\", \"userId\": \"${event.data.userId}\"}"
   )
   private String bodyTemplate;
   @EnumProperty(
      description = "<p>The encoding/escaping to be applied to string all values before they are inserted into the request body template.</p><p>Security warning: using no encoding allows unvalidated user input to be sent in the request body.</p><p>The JSON encoder also transforms string lists into (escaped) JSON arrays.</p>",
      defaultValue = "JSON",
      displayName = "Value Encoding/Escaping",
      optional = true
   )
   private RemoteEventSubscriberSettings.ValueEncoding valueEncoding;
   @PluginProperty(description = "The HTTP client that executes the request.", displayName = "HTTP Client", optional = false)
   private HttpClientConfig httpClient;
   @EnumProperty(description = "HTTP Method to be used.", defaultValue = "POST", displayName = "HTTP Method", optional = true)
   private RemoteEventSubscriberSettings.HttpMethod httpMethod;
   @StringProperty(
      description = "Value of the content-type header to be used in the request.",
      optional = true,
      defaultValue = "application/json",
      suggestedValues = {"application/json", "application/x-www-form-urlencoded", "application/xml", "text/plain"}
   )
   private String contentType;

   public String getUrl() {
      return this.url;
   }

   public Optional<String> getBodyTemplate() {
      return Optional.ofNullable(this.bodyTemplate);
   }

   public RemoteEventSubscriberSettings.ValueEncoding getValueEncoding() {
      return this.valueEncoding;
   }

   public HttpClientConfig getHttpClient() {
      return this.httpClient;
   }

   public RemoteEventSubscriberSettings.HttpMethod getHttpMethod() {
      return this.httpMethod;
   }

   public String getContentType() {
      return this.contentType;
   }

   LoginappRemoteEventSubscriberConfig(
      LoginappSubscribedEventConfig event,
      List<ValueMapProviderConfig> valueMapProviders,
      HttpClientConfig httpClient,
      RemoteEventSubscriberSettings.HttpMethod httpMethod,
      String url,
      String bodyTemplate,
      RemoteEventSubscriberSettings.ValueEncoding valueEncoding,
      String contentType
   ) {
      super(event, valueMapProviders);
      this.httpClient = httpClient;
      this.httpMethod = httpMethod;
      this.url = url;
      this.bodyTemplate = bodyTemplate;
      this.valueEncoding = valueEncoding;
      this.contentType = contentType;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class LoginappRemoteEventSubscriberConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private LoginappSubscribedEventConfig event;
      @SuppressFBWarnings(justification = "generated code")
      private ArrayList<ValueMapProviderConfig> valueMapProviders;
      @SuppressFBWarnings(justification = "generated code")
      private HttpClientConfig httpClient;
      @SuppressFBWarnings(justification = "generated code")
      private RemoteEventSubscriberSettings.HttpMethod httpMethod;
      @SuppressFBWarnings(justification = "generated code")
      private String url;
      @SuppressFBWarnings(justification = "generated code")
      private String bodyTemplate;
      @SuppressFBWarnings(justification = "generated code")
      private RemoteEventSubscriberSettings.ValueEncoding valueEncoding;
      @SuppressFBWarnings(justification = "generated code")
      private String contentType;

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

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder event(LoginappSubscribedEventConfig event) {
         this.event = event;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder valueMapProvider(ValueMapProviderConfig valueMapProvider) {
         if (this.valueMapProviders == null) {
            this.valueMapProviders = new ArrayList<>();
         }

         this.valueMapProviders.add(valueMapProvider);
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder valueMapProviders(
         Collection<? extends ValueMapProviderConfig> valueMapProviders
      ) {
         if (valueMapProviders == null) {
            throw new NullPointerException("valueMapProviders cannot be null");
         }

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

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

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder clearValueMapProviders() {
         if (this.valueMapProviders != null) {
            this.valueMapProviders.clear();
         }

         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder httpClient(HttpClientConfig httpClient) {
         this.httpClient = httpClient;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder httpMethod(RemoteEventSubscriberSettings.HttpMethod httpMethod) {
         this.httpMethod = httpMethod;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder url(String url) {
         this.url = url;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder bodyTemplate(String bodyTemplate) {
         this.bodyTemplate = bodyTemplate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder valueEncoding(
         RemoteEventSubscriberSettings.ValueEncoding valueEncoding
      ) {
         this.valueEncoding = valueEncoding;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder contentType(String contentType) {
         this.contentType = contentType;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LoginappRemoteEventSubscriberConfig build() {
         return new LoginappRemoteEventSubscriberConfig(this.event, switch (this.valueMapProviders == null ? 0 : this.valueMapProviders.size()) {
            case 0 -> Collections.emptyList();
            case 1 -> Collections.singletonList(this.valueMapProviders.get(0));
            default -> Collections.unmodifiableList(new ArrayList<>(this.valueMapProviders));
         }, this.httpClient, this.httpMethod, this.url, this.bodyTemplate, this.valueEncoding, this.contentType);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "LoginappRemoteEventSubscriberConfig.LoginappRemoteEventSubscriberConfigBuilder(event="
            + this.event
            + ", valueMapProviders="
            + this.valueMapProviders
            + ", httpClient="
            + this.httpClient
            + ", httpMethod="
            + this.httpMethod
            + ", url="
            + this.url
            + ", bodyTemplate="
            + this.bodyTemplate
            + ", valueEncoding="
            + this.valueEncoding
            + ", contentType="
            + this.contentType
            + ")";
      }
   }
}
