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

import com.airlock.iam.factor.application.configuration.fido.FidoSettingsConfig;
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.step.CommonFidoCheckingStepConfig;
import com.airlock.iam.flow.types.api.application.configuration.step.StepConfigForTransactionApprovalFlows;
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.PluginMapProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Map;

@PluginLicenseRestriction(moduleAffiliation = "FIDO")
@Plugin(
   description = "<p>Step to approve a transaction with FIDO.</p>\n<p>To achieve dynamic linking of transaction parameters with the challenge sent to the FIDO authenticator,\nthe transaction parameters are hashed together with a random nonce to generate the challenge.</p>\n<p>Challenge C:= Hash(T | N), where:\n<ul>\n  <li><code>Hash</code> is the SHA512 hashing function.</li>\n  <li><code>T</code> is the serialized form of the transaction parameters.</li>\n  <li><code>N</code> is a random nonce to prevent replay attacks.</li>\n</ul>\n</p>\n<p>FIDO approval does not allow verification of the data via a separate channel.\nIf this additional level of security is required, use other approval factors like Airlock 2FA, Cronto or mTAN.</p>\n",
   displayName = "FIDO Transaction Approval Step"
)
@Groups(openGroups = "Basic Settings", groupSequence = {"Basic Settings", "Tags/Guards", "Flow Control", "Advanced Settings"})
@NextActions(FidoTransactionApprovalStepActions.Action.class)
public class FidoTransactionApprovalStepConfig extends CommonFidoCheckingStepConfig implements StepConfigForTransactionApprovalFlows {
   @PluginMapProperty(
      description = "<p>If the step fails (no retry) and a goto target for the error code is defined here, the flow does not fail\nand instead a \"goto\" to the specified target step is executed. Note that even when the \"goto\" is executed, any\nerror codes that are considered a failed factor attempt will still increment the \"failed attempts\" counter,\nand may lead to the user being locked. Therefore, this may still result in a failed flow.</p>\n<p>This step can return the following error codes:\n<ul>\n  <li><code>FIDO_AUTHENTICATION_FAILED</code>: The FIDO authentication failed for unspecified reasons (either in the browser/client or during server-side verification).</li>\n  <li><code>FIDO_AUTHENTICATION_TIMEOUT</code>: The response from the browser/client has timed out.</li>\n  <li><code>FIDO_AUTHENTICATION_ABORTED</code>: The authentication has been aborted in the browser/client.</li>\n  <li><code>FIDO_AUTHENTICATION_NOT_ALLOWED</code>: The browser/client did not allow authentication with the given credentials.</li>\n  <li><code>FIDO_WEB_AUTHN_NOT_AVAILABLE</code>: The client/browser is not capable of performing WebAuthn/FIDO authentication.</li>\n  <li><code>NO_VALID_TOKEN</code>: The user has no eligible FIDO credential registered.</li>\n</ul></p>",
      optional = true
   )
   @GroupProperty(groupIdentifier = "Flow Control")
   private Map<String, StepIdConfig> onFailureGotos;
   @BooleanProperty(
      description = "<p>This option enables logging of all relevant data to allow cryptographic verification of the dynamic linking\nas described in the plugin documentation. The information is logged on INFO level and can be found using the\nsearch term 'FIDO Dynamic Linking Verification Data'.</p>\n<p>When enabled, the following information is logged as a JSON object:\n<ul>\n\t<li>N: Nonce which adds randomness to the challenge to prevent replay attacks</li>\n\t<li>T: Transaction data map formated as a JSON String</li>\n\t<li>CAD: Challenge authenticatorData - Randomness added by the FIDO key during signing</li>\n\t<li>CCD: Challenge clientDataJSON - Object with challenge required by FIDO key during signing. Contains at least 'type', 'challenge' and 'origin'.</li>\n\t<li>S: Signed challenge returned from the FIDO key</li>\n\t<li>P: Public Key used during the challenge response protocol</li>\n</ul></p>\n<p>The verification script has to check the following two properties to link the transaction data to the challenge:\n<ul>\n\t<li>VERIFY(P, CM, S), where CM := CAD + SHA256(CCD) as defined by FIDO webauthn.</li>\n\t<li>CCD contains C, where C:= SHA512(T | N) as defined by Dynamic Linking on IAM.</li>\n</ul></p>\n<p>A verification script can be downloaded from the <a href=\"https://docs.airlock.com/iam/latest/index/1755485390167.html\">Airlock IAM customer documentation</a></p>",
      optional = true,
      defaultValue = "false"
   )
   @GroupProperty(groupIdentifier = "Advanced Settings")
   private boolean verificationLogging;

   public Map<String, StepIdConfig> getOnFailureGotos() {
      return this.onFailureGotos;
   }

   private FidoTransactionApprovalStepConfig(
      FlowConditionConfig skipCondition,
      FlowConditionConfig preCondition,
      boolean requiresActivation,
      List<TagConfig> tagsOnSuccess,
      @Nullable StepIdConfig stepId,
      FidoSettingsConfig fidoSettings,
      List<InteractiveGotoTargetConfig> interactiveGotoTargets,
      List<DynamicStepActivationConfig> dynamicStepActivations,
      Map<String, StepIdConfig> onFailureGotos,
      List<ValueMapProviderConfig> customResponseAttributes,
      List<ValueMapProviderConfig> customFailureResponseAttributes,
      boolean verificationLogging
   ) {
      super(
         skipCondition,
         preCondition,
         requiresActivation,
         tagsOnSuccess,
         stepId,
         fidoSettings,
         interactiveGotoTargets,
         dynamicStepActivations,
         onFailureGotos,
         customResponseAttributes,
         customFailureResponseAttributes
      );
      this.onFailureGotos = onFailureGotos;
      this.verificationLogging = verificationLogging;
   }

   public boolean isVerificationLogging() {
      return this.verificationLogging;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public static class FidoTransactionApprovalStepConfigBuilder {
      @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 FidoSettingsConfig fidoSettings;
      @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 boolean verificationLogging;

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

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

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public FidoTransactionApprovalStepConfig.FidoTransactionApprovalStepConfigBuilder fidoSettings(FidoSettingsConfig fidoSettings) {
         this.fidoSettings = fidoSettings;
         return this;
      }

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

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

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

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

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

      @SuppressFBWarnings(justification = "generated code")
      public FidoTransactionApprovalStepConfig.FidoTransactionApprovalStepConfigBuilder verificationLogging(boolean verificationLogging) {
         this.verificationLogging = verificationLogging;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public FidoTransactionApprovalStepConfig build() {
         return new FidoTransactionApprovalStepConfig(
            this.skipCondition,
            this.preCondition,
            this.requiresActivation,
            this.tagsOnSuccess,
            this.stepId,
            this.fidoSettings,
            this.interactiveGotoTargets,
            this.dynamicStepActivations,
            this.onFailureGotos,
            this.customResponseAttributes,
            this.customFailureResponseAttributes,
            this.verificationLogging
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "FidoTransactionApprovalStepConfig.FidoTransactionApprovalStepConfigBuilder(skipCondition="
            + this.skipCondition
            + ", preCondition="
            + this.preCondition
            + ", requiresActivation="
            + this.requiresActivation
            + ", tagsOnSuccess="
            + this.tagsOnSuccess
            + ", stepId="
            + this.stepId
            + ", fidoSettings="
            + this.fidoSettings
            + ", interactiveGotoTargets="
            + this.interactiveGotoTargets
            + ", dynamicStepActivations="
            + this.dynamicStepActivations
            + ", onFailureGotos="
            + this.onFailureGotos
            + ", customResponseAttributes="
            + this.customResponseAttributes
            + ", customFailureResponseAttributes="
            + this.customFailureResponseAttributes
            + ", verificationLogging="
            + this.verificationLogging
            + ")";
      }
   }
}
