package com.airlock.iam.oauth2.application.configuration.signature;

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.common.domain.model.jwt.signature.SignatureAlgorithm;
import com.airlock.iam.common.domain.service.jwt.signature.PrivateKeyJwtSigner;
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.PropertyConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyTypeMismatchException;
import com.airlock.iam.plugin.api.testlet.Testlet;
import com.airlock.iam.plugin.api.testlet.Testlet.TestletResult;
import com.airlock.iam.plugin.api.testlet.Testlet.TestletType;
import com.airlock.iam.plugin.api.testlet.Testlet.TestletResult.TestResult;
import com.airlock.iam.plugin.api.valuereader.BooleanPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.EnumPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.FilePropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.StringPropertyValueReader;
import java.io.File;
import org.apache.commons.lang3.StringUtils;

public final class OAuth2KidTestlet implements Testlet {
   public TestletResult performTest(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyConfigurationException {
      try {
         String keyId = ViolationException.checkNotNull(this.buildSigner(config, env, context).keyId());
         return !isIncludeKid(config, env, context)
            ? successWithMessage(
               "<p>Issued JWTs will not contain a KID.</p><p>To include a KID in the issued JWTs, please enable the Include KID property.</p><p>If this property were enabled, issued JWTs would include the following KID:</p><p>"
                  + keyId
                  + "</p>"
            )
            : successWithMessage("<p>Issued JWTs will include the following KID:</p><p>" + keyId + "</p>");
      } catch (IllegalArgumentException | ViolationException e) {
         return failure();
      }
   }

   public boolean areRequiredPropertiesConfigured(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) {
      try {
         return !StringUtils.isBlank(getKeystoreFile(config, env, context)) && !StringUtils.isBlank(getSigningKeyPassword(config, env, context));
      } catch (PropertyConfigurationException e) {
         return false;
      }
   }

   public String getRequiredPropertiesMissingMessage() {
      return "Please configure the Keystore File and Signing Key Password properties to get a KID value.";
   }

   public TestletType getType() {
      return TestletType.RESULT;
   }

   private PrivateKeyJwtSigner buildSigner(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyConfigurationException {
      return PrivateKeyJwtSigner.builder(getAlgorithm(config, env, context), new File(getKeystoreFile(config, env, context)))
         .keyStorePassword(getKeystorePassword(config, env, context))
         .privateKeyAlias(getSigningKeyAlias(config, env, context))
         .privateKeyPassword(getSigningKeyPassword(config, env, context))
         .includeKid(true)
         .build();
   }

   private static SignatureAlgorithm getAlgorithm(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyTypeMismatchException {
      String algorithm = EnumPropertyValueReader.effectiveEnumPropertyValue(config, "algorithm", env, context);
      return PrivateKeyJwtSignatureConfig.Algorithm.valueOf(algorithm).getAlgorithm();
   }

   private static String getKeystoreFile(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyTypeMismatchException {
      return FilePropertyValueReader.effectiveFilePropertyValue(config, "keystoreFile", env, context);
   }

   private static String getKeystorePassword(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyTypeMismatchException {
      return StringPropertyValueReader.effectiveStringConfigValue(config, "keystorePassword", env, context);
   }

   private static String getSigningKeyPassword(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyTypeMismatchException {
      return StringPropertyValueReader.effectiveStringConfigValue(config, "signingKeyPassword", env, context);
   }

   private static String getSigningKeyAlias(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyTypeMismatchException {
      return StringPropertyValueReader.effectiveStringConfigValue(config, "signingKeyAlias", env, context);
   }

   private static Boolean isIncludeKid(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyConfigurationException {
      return BooleanPropertyValueReader.effectiveBooleanPropertyValue(config, "includeKid", env, context);
   }

   private static TestletResult successWithMessage(String message) {
      return new TestletResult(TestResult.SUCCESSFUL, "Key ID Value", message);
   }

   private static TestletResult failure() {
      return new TestletResult(
         TestResult.FAILED, "Key ID Value", "The resulting KID could not be computed. Are the keystore and signing key properties correctly configured?"
      );
   }
}
