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

import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.base.domain.model.mapification.MapificationUtils;
import com.airlock.iam.base.domain.model.mapification.MapifiedValuesBuilder;
import com.airlock.iam.base.misc.util.Base64Util;
import com.airlock.iam.common.domain.model.token.secretquestions.SecretQuestion;
import com.airlock.iam.common.domain.model.token.secretquestions.SecretQuestionsNormalization;
import com.airlock.iam.flow.api.domain.model.step.StepSession;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

public final class SecretQuestionsIdentityVerificationStepSession implements StepSession {
   private static final String QUESTIONS_KEY = "questions";
   private static final String FAILED_ATTEMPTS_KEY = "failedAttempts";
   private static final String SIMULATED_KEY = "simulated";
   private static final String QUESTION_RESOURCE_KEY_KEY = "questionResourceKey";
   private static final String IS_VALID_KEY = "isValid";
   private static final String CREATION_DATE_KEY = "creationDate";
   private static final String NUMBER_OF_FAILED_ATTEMPTS_KEY = "numberOfFailedAttempts";
   private static final String IS_BLOCKED_KEY = "isBlocked";
   private static final String LATEST_SUCCESSFUL_USAGE_DATE_KEY = "latestSuccessfulUsageDate";
   private static final String LATEST_USAGE_DATE_KEY = "latestUsageDate";
   private static final String ANSWER_KEY = "answer";
   private static final String NORMALIZATION_KEY = "normalization";
   private static final String TOUCH_DATE_KEY = "touchDate";
   private List<SecretQuestion> questions = Collections.emptyList();
   private int failedAttempts;
   private boolean simulated;

   public void setSimulated() {
      this.simulated = true;
   }

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

   private static MapifiedValues mapifySecretQuestion(SecretQuestion question) {
      return MapifiedValuesBuilder.mapifiedValues()
         .put("questionResourceKey", question.getQuestionResourceKey())
         .put("isValid", question.isValid())
         .putIfPresent("creationDate", Optional.ofNullable(question.getCreationDate()).map(MapificationUtils::mapifyDate))
         .put("numberOfFailedAttempts", question.getNumberOfFailedAttempts())
         .put("isBlocked", question.isBlocked())
         .putIfPresent("latestSuccessfulUsageDate", question.getLatestSuccessfulUsageDate().map(MapificationUtils::mapifyDate))
         .putIfPresent("latestUsageDate", question.getLatestUsageDate().map(MapificationUtils::mapifyDate))
         .putIfPresent("answer", Optional.ofNullable(question.getAnswer()).map(Base64Util::encodeToString))
         .putIfPresent("normalization", Optional.ofNullable(question.getNormalization()).map(Enum::name))
         .put("touchDate", MapificationUtils.mapifyDate(question.getTouchDate()))
         .build();
   }

   private static SecretQuestion demapifySecretQuestion(MapifiedValues values) {
      return new SecretQuestion(
         values.get("questionResourceKey"),
         values.<Boolean>get("isValid"),
         values.demapifyOptional("creationDate", MapificationUtils::demapifyDate).orElse(null),
         values.<Integer>get("numberOfFailedAttempts"),
         values.<Boolean>get("isBlocked"),
         values.demapifyOptional("latestSuccessfulUsageDate", MapificationUtils::demapifyDate).orElse(null),
         values.demapifyOptional("latestUsageDate", MapificationUtils::demapifyDate).orElse(null),
         values.<String>getOptional("answer").map(Base64Util::decode).orElse(null),
         values.<String>getOptional("normalization").map(n -> MapificationUtils.demapifyEnumOfClass(n, SecretQuestionsNormalization.class)).orElse(null),
         values.demapifyValue("touchDate", MapificationUtils::demapifyDate)
      );
   }

   @Override
   public MapifiedValues mapify() {
      return MapifiedValuesBuilder.mapifiedValues()
         .putIfNotEmpty("questions", this.questions.stream().map(SecretQuestionsIdentityVerificationStepSession::mapifySecretQuestion).toList())
         .put("failedAttempts", this.failedAttempts)
         .put("simulated", this.simulated)
         .build();
   }

   public static SecretQuestionsIdentityVerificationStepSession demapify(MapifiedValues values) {
      return new SecretQuestionsIdentityVerificationStepSession(
         values.demapifyList("questions", SecretQuestionsIdentityVerificationStepSession::demapifySecretQuestion),
         values.<Integer>get("failedAttempts"),
         values.<Boolean>get("simulated")
      );
   }

   @SuppressFBWarnings(justification = "generated code")
   public List<SecretQuestion> getQuestions() {
      return this.questions;
   }

   @SuppressFBWarnings(justification = "generated code")
   public int getFailedAttempts() {
      return this.failedAttempts;
   }

   @SuppressFBWarnings(justification = "generated code")
   public boolean isSimulated() {
      return this.simulated;
   }

   @SuppressFBWarnings(justification = "generated code")
   SecretQuestionsIdentityVerificationStepSession(List<SecretQuestion> questions, int failedAttempts, boolean simulated) {
      this.questions = questions;
      this.failedAttempts = failedAttempts;
      this.simulated = simulated;
   }

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

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

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

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

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

   @SuppressFBWarnings(justification = "generated code")
   public void setQuestions(List<SecretQuestion> questions) {
      this.questions = questions;
   }
}
