package com.airlock.iam.transactionapproval.application.configuration.cronto;

import com.airlock.iam.common.api.application.configuration.cronto.CrontoMessageProviderConfig;
import com.airlock.iam.core.domain.model.cronto.CrontoCheckingSettings;
import com.airlock.iam.core.misc.cronto.CrontoHandler;
import com.airlock.iam.core.misc.cronto.CrontoHandler.CrontoChallengeType;
import com.airlock.iam.flow.api.application.configuration.dynamicactivation.DynamicStepActivationConfig;
import com.airlock.iam.flow.api.application.configuration.selection.FlowConditionConfig;
import com.airlock.iam.flow.api.application.configuration.step.InteractiveGotoTargetConfig;
import com.airlock.iam.flow.api.application.configuration.step.StepIdConfig;
import com.airlock.iam.flow.api.application.configuration.tag.TagConfig;
import com.airlock.iam.flow.api.application.configuration.valueprovider.ValueMapProviderConfig;
import com.airlock.iam.flow.shared.application.configuration.cronto.CommonCrontoCheckingStepConfig;
import com.airlock.iam.flow.shared.domain.model.cronto.CrontoCheckingStep.CrontoAuthStepActions;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForTransactionApprovalFlows;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.Groups;
import com.airlock.iam.plugin.api.annotation.config.NextActions;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.BooleanProperty;
import com.airlock.iam.plugin.api.annotation.property.GroupProperty;
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.ConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.valuereader.BooleanPropertyValueReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Map;

@Plugin(description = "Configuration for a Cronto transaction approval flow step.", displayName = "Cronto Transaction Approval Step")
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control", "Advanced Settings"})
@PluginLicenseRestriction(moduleAffiliation = "Cronto")
@NextActions(CrontoAuthStepActions.class)
public class CrontoTransactionApprovalStepConfig extends CommonCrontoCheckingStepConfig implements StepConfigForTransactionApprovalFlows {
   private static final String AUTHENTICATE_PUSH_DEVICES_ONLY_DISPLAY_NAME = "Allow Push Devices Only";
   private static final String PUSH_SELECTION_FOR_SINGLE_DEVICE_DISPLAY_NAME = "Push Selection For Single Device";
   @PluginProperty(description = "Creates the message for transaction approval.", optional = false)
   @GroupProperty(groupIdentifier = "Basic Settings")
   private CrontoMessageProviderConfig messageProvider;
   @BooleanProperty(
      description = "<p>If this flag is set and there is no push-enabled device for the user, transaction approval is not possible.</p><p>This feature may be used for mobile application approvals, where showing a cryptogram on the same device is not appropriate.</p>",
      displayName = "Allow Push Devices Only",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private boolean authenticatePushDevicesOnly;
   private static final String PUSH_ONLY_FIELD = "authenticatePushDevicesOnly";
   @BooleanProperty(
      description = "If enabled, the step also asks for push device selection if there is only one push device enabled. Since the selection always includes the \"offline\" option, this can be used for \"app-to-app\" setups, where push messages should never be sent.",
      displayName = "Push Selection For Single Device",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private boolean pushSelectionForSingleDevice;
   private static final String SELECTION_FOR_SINGLE_DEVIC_FIELD = "pushSelectionForSingleDevice";
   @BooleanProperty(
      description = "If enabled, the step never asks for a device selection even when there is more then one push device available. To enable this feature, Allow Push Devices Only needs to be enabled and Push Selection For Single Device needs to be disabled. In case a Auth Token ID was provided, the push notification is only sent to that device.",
      defaultValue = "false",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private boolean pushToAllDevices;
   private static final String PUSH_TO_ALL_FIELD = "pushToAllDevices";

   public CrontoCheckingSettings getSettings() {
      return CrontoCheckingSettings.builder()
         .authenticatePushDevicesOnly(this.authenticatePushDevicesOnly)
         .pushSelectionForSingleDevice(this.pushSelectionForSingleDevice)
         .pushToAllDevices(this.pushToAllDevices)
         .showLoginIdOnPush(false)
         .maxResponseRetries(this.getMaxResponseRetries())
         .crontoHandler(this.getCrontoHandler())
         .challengeType(CrontoChallengeType.TRANSACTION_APPROVAL)
         .build();
   }

   protected CrontoTransactionApprovalStepConfig(
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      boolean requiresActivation,
      List<TagConfig> tagsOnSuccess,
      @Nullable StepIdConfig stepId,
      List<InteractiveGotoTargetConfig> interactiveGotoTargets,
      List<DynamicStepActivationConfig> dynamicStepActivations,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes,
      CrontoHandler crontoHandler,
      CrontoMessageProviderConfig messageProvider,
      int maxResponseRetries,
      boolean authenticatePushDevicesOnly,
      boolean pushSelectionForSingleDevice,
      boolean pushToAllDevices,
      String authenticationMethodId
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes,
         crontoHandler,
         maxResponseRetries,
         authenticationMethodId
      );
      this.messageProvider = messageProvider;
      this.authenticatePushDevicesOnly = authenticatePushDevicesOnly;
      this.pushSelectionForSingleDevice = pushSelectionForSingleDevice;
      this.pushToAllDevices = pushToAllDevices;
   }

   @ConfigurationValidator
   public static void validateSelectionSettings(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment valenv) throws ConfigurationException {
      if (BooleanPropertyValueReader.effectiveBooleanPropertyValue(config, "pushToAllDevices", env, context)) {
         if (BooleanPropertyValueReader.effectiveBooleanPropertyValue(config, "pushSelectionForSingleDevice", env, context)) {
            throw new PropertyConstraintViolationException(
               "pushToAllDevices", config, env, context, "pushSelectionForSingleDevice needs to be disabled to enable pushing to all devices."
            );
         }
      }
   }

   public CrontoMessageProviderConfig getMessageProvider() {
      return this.messageProvider;
   }

   public boolean isAuthenticatePushDevicesOnly() {
      return this.authenticatePushDevicesOnly;
   }

   public boolean isPushSelectionForSingleDevice() {
      return this.pushSelectionForSingleDevice;
   }

   public boolean isPushToAllDevices() {
      return this.pushToAllDevices;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class CrontoTransactionApprovalStepConfigBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig skipCondition;
      @SuppressFBWarnings(justification = "generated code")
      private FlowConditionConfig preCondition;
      @SuppressFBWarnings(justification = "generated code")
      private boolean requiresActivation;
      @SuppressFBWarnings(justification = "generated code")
      private List<TagConfig> tagsOnSuccess;
      @SuppressFBWarnings(justification = "generated code")
      private StepIdConfig stepId;
      @SuppressFBWarnings(justification = "generated code")
      private List<InteractiveGotoTargetConfig> interactiveGotoTargets;
      @SuppressFBWarnings(justification = "generated code")
      private List<DynamicStepActivationConfig> dynamicStepActivations;
      @SuppressFBWarnings(justification = "generated code")
      private Map<String, StepIdConfig> onFailureGotos;
      @SuppressFBWarnings(justification = "generated code")
      private List<ValueMapProviderConfig> customResponseAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private List<ValueMapProviderConfig> customFailureResponseAttributes;
      @SuppressFBWarnings(justification = "generated code")
      private CrontoHandler crontoHandler;
      @SuppressFBWarnings(justification = "generated code")
      private CrontoMessageProviderConfig messageProvider;
      @SuppressFBWarnings(justification = "generated code")
      private int maxResponseRetries;
      @SuppressFBWarnings(justification = "generated code")
      private boolean authenticatePushDevicesOnly;
      @SuppressFBWarnings(justification = "generated code")
      private boolean pushSelectionForSingleDevice;
      @SuppressFBWarnings(justification = "generated code")
      private boolean pushToAllDevices;
      @SuppressFBWarnings(justification = "generated code")
      private String authenticationMethodId;

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

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder skipCondition(FlowConditionConfig skipCondition) {
         this.skipCondition = skipCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder preCondition(FlowConditionConfig preCondition) {
         this.preCondition = preCondition;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder requiresActivation(boolean requiresActivation) {
         this.requiresActivation = requiresActivation;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder tagsOnSuccess(List<TagConfig> tagsOnSuccess) {
         this.tagsOnSuccess = tagsOnSuccess;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder stepId(@Nullable StepIdConfig stepId) {
         this.stepId = stepId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder interactiveGotoTargets(
         List<InteractiveGotoTargetConfig> interactiveGotoTargets
      ) {
         this.interactiveGotoTargets = interactiveGotoTargets;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder dynamicStepActivations(
         List<DynamicStepActivationConfig> dynamicStepActivations
      ) {
         this.dynamicStepActivations = dynamicStepActivations;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder onFailureGotos(Map<String, StepIdConfig> onFailureGotos) {
         this.onFailureGotos = onFailureGotos;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder customResponseAttributes(
         List<ValueMapProviderConfig> customResponseAttributes
      ) {
         this.customResponseAttributes = customResponseAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder customFailureResponseAttributes(
         List<ValueMapProviderConfig> customFailureResponseAttributes
      ) {
         this.customFailureResponseAttributes = customFailureResponseAttributes;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder crontoHandler(CrontoHandler crontoHandler) {
         this.crontoHandler = crontoHandler;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder messageProvider(CrontoMessageProviderConfig messageProvider) {
         this.messageProvider = messageProvider;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder maxResponseRetries(int maxResponseRetries) {
         this.maxResponseRetries = maxResponseRetries;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder authenticatePushDevicesOnly(boolean authenticatePushDevicesOnly) {
         this.authenticatePushDevicesOnly = authenticatePushDevicesOnly;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder pushSelectionForSingleDevice(boolean pushSelectionForSingleDevice) {
         this.pushSelectionForSingleDevice = pushSelectionForSingleDevice;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder pushToAllDevices(boolean pushToAllDevices) {
         this.pushToAllDevices = pushToAllDevices;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder authenticationMethodId(String authenticationMethodId) {
         this.authenticationMethodId = authenticationMethodId;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public CrontoTransactionApprovalStepConfig build() {
         return new CrontoTransactionApprovalStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            this.tagsOnSuccess,
            this.stepId,
            this.interactiveGotoTargets,
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.crontoHandler,
            this.messageProvider,
            this.maxResponseRetries,
            this.authenticatePushDevicesOnly,
            this.pushSelectionForSingleDevice,
            this.pushToAllDevices,
            this.authenticationMethodId
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "CrontoTransactionApprovalStepConfig.CrontoTransactionApprovalStepConfigBuilder(skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", requiresActivation="
            + this.requiresActivation
            + ", tagsOnSuccess="
            + this.tagsOnSuccess
            + ", stepId="
            + this.stepId
            + ", interactiveGotoTargets="
            + this.interactiveGotoTargets
            + ", dynamicStepActivations="
            + this.dynamicStepActivations
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ", crontoHandler="
            + this.crontoHandler
            + ", messageProvider="
            + this.messageProvider
            + ", maxResponseRetries="
            + this.maxResponseRetries
            + ", authenticatePushDevicesOnly="
            + this.authenticatePushDevicesOnly
            + ", pushSelectionForSingleDevice="
            + this.pushSelectionForSingleDevice
            + ", pushToAllDevices="
            + this.pushToAllDevices
            + ", authenticationMethodId="
            + this.authenticationMethodId
            + ")";
      }
   }
}
