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

import com.airlock.iam.base.domain.service.uri.IamUriBuilder;
import com.airlock.iam.core.misc.util.httpclient.ConnectionTestlet;
import com.airlock.iam.core.misc.util.httpclient.HttpClientConfig;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.PluginProperty;
import com.airlock.iam.plugin.api.annotation.property.StringProperty;
import com.airlock.iam.plugin.api.annotation.property.TestletProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.InitializeException;
import com.airlock.iam.plugin.api.exception.PropertyConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyTypeMismatchException;
import com.airlock.iam.plugin.api.testlet.Testlet.TestletResult;
import com.airlock.iam.plugin.api.testlet.Testlet.TestletResult.TestResult;
import com.airlock.iam.plugin.api.valuereader.PluginPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.StringPropertyValueReader;
import com.airlock.iam.plugin.framework.misc.ioc.PluginLoader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.security.GeneralSecurityException;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.core5.http.HttpResponse;

@Plugin(
   description = "The connection details of an Airlock Gateway REST API. Connecting to the Gateway's REST API requires a valid\nAPI key which can be obtained from the Gateway's Configuration Center. Each Gateway has its own API key, it\ncannot be used for multiple connections. For security reasons, the gateway user, corresponding to the API token,\nshould be configured to have minimal access rights.\nFor this feature to work, only the following gateway endpoints need to be accessible.\n<ul>\n\t<li>/session/create</li>\n\t<li>/session/terminate</li>\n\t<li>/system/status/session</li>\n</ul>\nThis can be achieved by configuring the user on the gateway similar to this:\n<p><tt>\nuser:airlockiam roles:airlock-administrator tenant: password:disabled readPaths:.*</br>\nwritePaths:^\\/session\\/(create|terminate)$|^\\/system\\/status\\/sessions\\/.*$ \n</tt></p>\nAdditionally, IAM needs HTTP(S) access to the Gateway's REST API which may not always be the case, depending on the network topology.\n",
   displayName = "Gateway REST API Connection"
)
public class GatewayRestApiConnectionConfig {
   @StringProperty(
      description = "The base URL of the Airlock Gateway's REST API. It normally is accessible at https://&lt;airlock-gateway&gt;/airlock/rest.\nFirewall rules may have to be adapted accordingly so IAM can connect to this URL.",
      displayName = "URL",
      exampleValues = "https://gateway.mycompany.com/airlock/rest/",
      optional = false
   )
   @TestletProperty(buttonToolTip = "Test Gateway Connection", testletClass = GatewayRestApiConnectionConfig.GatewayAdminRestApiConnectionTestlet.class)
   private String url;
   static final String GATEWAY_REST_API_URL_FIELD = "url";
   static final String GATEWAY_REST_API_URL_DISPLAY_NAME = "URL";
   @PluginProperty(
      description = "The HTTP Client used to connect to the Gateway's REST API. The Gateway's API key must be configured as the\nHTTP Client's bearer token. It can be obtained from the Gateway's Configuration Center.",
      displayName = "HTTP Client",
      optional = false
   )
   private HttpClientConfig httpClient;
   static final String HTTP_CLIENT_FIELD = "httpClient";
   static final String HTTP_CLIENT_DISPLAY_NAME = "HTTP Client";

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

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

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

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

   @SuppressFBWarnings(justification = "generated code")
   public GatewayRestApiConnectionConfig(String url, HttpClientConfig httpClient) {
      this.url = url;
      this.httpClient = httpClient;
   }

   public static final class GatewayAdminRestApiConnectionTestlet extends ConnectionTestlet {
      private static final String TESTLET_TITLE = "Gateway Admin REST API Connection Testlet";

      public TestletResult performTest(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyConfigurationException {
         String gatewayAdminRestApiUrl = StringPropertyValueReader.effectiveStringConfigValue(config, "url", env, context);
         PluginConfiguration httpClientPlugin = PluginPropertyValueReader.effectivePluginPropertyValue(config, "httpClient", env, context);

         try {
            HttpClientConfig httpClientConfig = (HttpClientConfig)PluginLoader.get().loadPlugin(httpClientPlugin, env, context);
            return this.connectToGatewayAdminRestApi(httpClientConfig, gatewayAdminRestApiUrl);
         } catch (InitializeException | GeneralSecurityException | IOException e) {
            return new TestletResult(
               TestResult.FAILED, "Gateway Admin REST API Connection Testlet", "Failed to connect to the Gateway Admin REST API: " + e.getMessage()
            );
         }
      }

      public boolean areRequiredPropertiesConfigured(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) {
         return isURLPropertyConfigured(config, env, context, "url") && this.isHttpClientConfigured(config, env, context);
      }

      public String getRequiredPropertiesMissingMessage() {
         return "Please configure the \"URL\" and \"HTTP Client\" properties.";
      }

      private boolean isHttpClientConfigured(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) {
         try {
            return PluginPropertyValueReader.effectivePluginPropertyValue(config, "httpClient", env, context) != null;
         } catch (PropertyTypeMismatchException e) {
            return false;
         }
      }

      private TestletResult connectToGatewayAdminRestApi(HttpClientConfig httpClientConfig, String gatewayAdminRestApiUrl) throws GeneralSecurityException, IOException {
         CloseableHttpClient httpClient = httpClientConfig.createHttpClient();

         TestletResult var10;
         label43: {
            try {
               HttpPost createRequest = new HttpPost(IamUriBuilder.fromUri(gatewayAdminRestApiUrl).path("session/create").build());
               HttpPost terminateRequest = new HttpPost(IamUriBuilder.fromUri(gatewayAdminRestApiUrl).path("session/terminate").build());
               int responseCode = (Integer)httpClient.execute(createRequest, HttpResponse::getCode);
               if (isOk(responseCode)) {
                  httpClient.execute(terminateRequest, HttpResponse::getCode);
                  var10 = new TestletResult(
                     TestResult.SUCCESSFUL, "Gateway Admin REST API Connection Testlet", "Successfully created a session with the Gateway's admin REST API"
                  );
                  break label43;
               }

               var10 = new TestletResult(
                  TestResult.FAILED,
                  "Gateway Admin REST API Connection Testlet",
                  String.format("Failed to connect to '%s'. Status code: %d", createRequest.getRequestUri(), responseCode)
               );
            } catch (Throwable var9) {
               if (httpClient != null) {
                  try {
                     httpClient.close();
                  } catch (Throwable var8) {
                     var9.addSuppressed(var8);
                  }
               }

               throw var9;
            }

            if (httpClient != null) {
               httpClient.close();
            }

            return var10;
         }

         if (httpClient != null) {
            httpClient.close();
         }

         return var10;
      }

      private static boolean isOk(int responseCode) {
         return responseCode >= 200 && responseCode < 300;
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class GatewayRestApiConnectionConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String url;
      @SuppressFBWarnings(justification = "generated code")
      private HttpClientConfig httpClient;

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public GatewayRestApiConnectionConfig build() {
         return new GatewayRestApiConnectionConfig(this.url, this.httpClient);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "GatewayRestApiConnectionConfig.GatewayRestApiConnectionConfigBuilder(url=" + this.url + ", httpClient=" + this.httpClient + ")";
      }
   }
}
