package com.airlock.iam.publicselfservice.domain.model.flow.session;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.common.api.domain.model.otp.OneTimePassword;
import com.airlock.iam.common.domain.model.otp.OtpMapifier;
import com.airlock.iam.flow.api.domain.model.step.StepSession;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;

public class OtpIdentityVerificationStepSession implements StepSession {
   private static final String OTP_KEY = "otp";
   private static final String FAILED_ATTEMPTS_KEY = "failedAttempts";
   private static final String MAX_FAILED_ATTEMPTS_KEY = "maxFailedAttempts";
   private Optional<OneTimePassword> otp;
   private int failedAttempts;
   private final int maxFailedAttempts;

   public OtpIdentityVerificationStepSession(int maxFailedAttempts) {
      this.otp = Optional.empty();
      this.failedAttempts = 0;
      this.maxFailedAttempts = maxFailedAttempts;
   }

   public boolean hasOtp() {
      return this.otp.isPresent();
   }

   public void setOtp(OneTimePassword generatedOtp) {
      if (this.otp.isPresent()) {
         throw new FailureException("OTP cannot be replaced");
      }

      this.otp = Optional.of(generatedOtp);
   }

   public void increaseFailedAttempts() {
      this.failedAttempts++;
   }

   public boolean isRetryPossible() {
      return this.failedAttempts <= this.maxFailedAttempts;
   }

   public int getRemainingAttempts() {
      return this.maxFailedAttempts - this.failedAttempts + 1;
   }

   @Override
   public MapifiedValues mapify() {
      return MapifiedValuesBuilder.mapifiedValues()
         .putIfPresent("otp", this.otp.map(OtpMapifier::mapifyOtp))
         .put("failedAttempts", this.failedAttempts)
         .put("maxFailedAttempts", this.maxFailedAttempts)
         .build();
   }

   public static OtpIdentityVerificationStepSession demapify(MapifiedValues values) {
      return new OtpIdentityVerificationStepSession(
         values.demapifyOptional("otp", OtpMapifier::demapifyOtp), values.<Integer>get("failedAttempts"), values.<Integer>get("maxFailedAttempts")
      );
   }

   @SuppressFBWarnings(justification = "generated code")
   private OtpIdentityVerificationStepSession(Optional<OneTimePassword> otp, int failedAttempts, int maxFailedAttempts) {
      this.otp = otp;
      this.failedAttempts = failedAttempts;
      this.maxFailedAttempts = maxFailedAttempts;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof OtpIdentityVerificationStepSession other)) {
         return false;
      } else {
         if (!other.canEqual(this)) {
            return false;
         }

         if (this.failedAttempts != other.failedAttempts) {
            return false;
         }

         if (this.maxFailedAttempts != other.maxFailedAttempts) {
            return false;
         }

         Object this$otp = this.otp;
         Object other$otp = other.otp;
         return this$otp == null ? other$otp == null : this$otp.equals(other$otp);
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   protected boolean canEqual(Object other) {
      return other instanceof OtpIdentityVerificationStepSession;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public int hashCode() {
      int PRIME = 59;
      int result = 1;
      result = result * 59 + this.failedAttempts;
      result = result * 59 + this.maxFailedAttempts;
      Object $otp = this.otp;
      return result * 59 + ($otp == null ? 43 : $otp.hashCode());
   }

   @SuppressFBWarnings(justification = "generated code")
   public Optional<OneTimePassword> getOtp() {
      return this.otp;
   }
}
