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

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.PluginProperty;
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.PluginPropertyValueReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

@Plugin(
   description = "<p>State encryption intended for migration from one encryption to a different one. Values are always written with the new encryption. If reading with the new encryption fails, decryption with the previous encryption is attempted as a fallback. After the migration period, this plugin should be replaced by the new encryption.</p><p>Specifically, this plugin can be used to migrate from unencrypted state to encrypted state. This plugin can <em>not</em> be used to migrate from encrypted state to unencrypted state.</p>"
)
public class MigratingStateEncryptionConfig implements StateEncryptionConfig {
   @PluginProperty(description = "The encryption to use as a fallback when <em>reading</em> values from Redis.", optional = false)
   private StateEncryptionConfig previousEncryption;
   @PluginProperty(
      description = "The encryption to use before reading and writing data from/to Redis. <p>Migrating to unencrypted state is <em>not</em> supported.</p>",
      optional = false
   )
   private StateEncryptionConfig newEncryption;
   private static final String NEW_ENCRYPTION_PROPERTY_NAME = "newEncryption";

   @ConfigurationValidator
   static void validateNewEncryption(PluginConfiguration pc, ConfigurationEnvironment env, ConfigurationContext context, Environment valenv) throws PropertyConstraintViolationException, PropertyTypeMismatchException {
      PluginConfiguration newEncryption = PluginPropertyValueReader.effectivePluginPropertyValue(pc, "newEncryption", env, context);
      if (newEncryption.getPluginClassName().equals(NoStateEncryptionConfig.class.getName())) {
         throw new PropertyConstraintViolationException("newEncryption", pc, env, context, "Migration to unencrypted state is not supported.");
      }
   }

   public StateEncryptionConfig getPreviousEncryption() {
      return this.previousEncryption;
   }

   public StateEncryptionConfig getNewEncryption() {
      return this.newEncryption;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public MigratingStateEncryptionConfig(StateEncryptionConfig previousEncryption, StateEncryptionConfig newEncryption) {
      this.previousEncryption = previousEncryption;
      this.newEncryption = newEncryption;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class MigratingStateEncryptionConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private StateEncryptionConfig previousEncryption;
      @SuppressFBWarnings(justification = "generated code")
      private StateEncryptionConfig newEncryption;

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

      @SuppressFBWarnings(justification = "generated code")
      public MigratingStateEncryptionConfig.MigratingStateEncryptionConfigBuilder previousEncryption(StateEncryptionConfig previousEncryption) {
         this.previousEncryption = previousEncryption;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigratingStateEncryptionConfig.MigratingStateEncryptionConfigBuilder newEncryption(StateEncryptionConfig newEncryption) {
         this.newEncryption = newEncryption;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public MigratingStateEncryptionConfig build() {
         return new MigratingStateEncryptionConfig(this.previousEncryption, this.newEncryption);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "MigratingStateEncryptionConfig.MigratingStateEncryptionConfigBuilder(previousEncryption="
            + this.previousEncryption
            + ", newEncryption="
            + this.newEncryption
            + ")";
      }
   }
}
