package com.airlock.iam.common.domain.model.token.secretquestions;

import com.airlock.iam.core.misc.util.password.hash.PasswordCheckerWithLegacyLatin1Support;
import com.airlock.iam.core.misc.util.password.hash.PasswordHash;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import java.util.Arrays;
import java.util.Date;
import java.util.Optional;

public class SecretQuestion {
   private final String questionResourceKey;
   private final boolean isValid;
   private final Date creationDate;
   private final int numberOfFailedAttempts;
   private final boolean isBlocked;
   private final Date latestSuccessfulUsageDate;
   private final Date latestUsageDate;
   private final byte[] answer;
   private final SecretQuestionsNormalization normalization;
   @Nullable
   private Date touchDate;

   public Optional<Date> getLatestSuccessfulUsageDate() {
      return Optional.ofNullable(this.latestSuccessfulUsageDate);
   }

   public Optional<Date> getLatestUsageDate() {
      return Optional.ofNullable(this.latestUsageDate);
   }

   public SecretQuestion withoutFailedAttempts() {
      return this.toBuilder().numberOfFailedAttempts(0).build();
   }

   public SecretQuestion incrementFailedAttempts() {
      return this.toBuilder().numberOfFailedAttempts(this.numberOfFailedAttempts + 1).build();
   }

   public SecretQuestion updateAnswerHash(SecretQuestionAnswer newAnswer, PasswordHash hash) {
      String normalizedAnswer = this.normalization.normalize(newAnswer.getAnswer());
      return this.toBuilder().answer(hash.getPasswordHash(normalizedAnswer)).build();
   }

   public SecretQuestion withoutBlock() {
      return this.toBuilder().isBlocked(false).build();
   }

   public SecretQuestion touchSuccessfulUsageDate() {
      return this.toBuilder().latestSuccessfulUsageDate(this.getTouchDate()).touchDate(this.getTouchDate()).build();
   }

   public SecretQuestion touchLatestUsageDate() {
      return this.toBuilder().latestUsageDate(this.getTouchDate()).touchDate(this.getTouchDate()).build();
   }

   public boolean isAnsweredBy(SecretQuestionAnswer userAnswer, boolean checkUsingLatin1Encoding, PasswordHash hash) {
      return !userAnswer.isSet() ? false : this.isEqualAnswer(userAnswer.getAnswer(), checkUsingLatin1Encoding, hash);
   }

   public boolean isEqualAnswer(String newAnswer, boolean checkUsingLatin1Encoding, PasswordHash hash) {
      if (newAnswer != null && this.answer != null) {
         PasswordCheckerWithLegacyLatin1Support passwordChecker = PasswordCheckerWithLegacyLatin1Support.builder()
            .hashFunction(hash)
            .passwordToCheck(this.normalization.normalize(newAnswer))
            .storedPasswordHash(this.answer)
            .build();
         return passwordChecker.isPasswordValid() || checkUsingLatin1Encoding && passwordChecker.areLegacyPasswordsValid();
      } else {
         return false;
      }
   }

   public Date getTouchDate() {
      if (this.touchDate == null) {
         this.touchDate = new Date();
      }

      return this.touchDate;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public SecretQuestion.SecretQuestionBuilder toBuilder() {
      return new SecretQuestion.SecretQuestionBuilder()
         .questionResourceKey(this.questionResourceKey)
         .isValid(this.isValid)
         .creationDate(this.creationDate)
         .numberOfFailedAttempts(this.numberOfFailedAttempts)
         .isBlocked(this.isBlocked)
         .latestSuccessfulUsageDate(this.latestSuccessfulUsageDate)
         .latestUsageDate(this.latestUsageDate)
         .answer(this.answer)
         .normalization(this.normalization)
         .touchDate(this.touchDate);
   }

   @SuppressFBWarnings(justification = "generated code")
   public SecretQuestion(
      String questionResourceKey,
      boolean isValid,
      Date creationDate,
      int numberOfFailedAttempts,
      boolean isBlocked,
      Date latestSuccessfulUsageDate,
      Date latestUsageDate,
      byte[] answer,
      SecretQuestionsNormalization normalization,
      @Nullable Date touchDate
   ) {
      this.questionResourceKey = questionResourceKey;
      this.isValid = isValid;
      this.creationDate = creationDate;
      this.numberOfFailedAttempts = numberOfFailedAttempts;
      this.isBlocked = isBlocked;
      this.latestSuccessfulUsageDate = latestSuccessfulUsageDate;
      this.latestUsageDate = latestUsageDate;
      this.answer = answer;
      this.normalization = normalization;
      this.touchDate = touchDate;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public String toString() {
      return "SecretQuestion(questionResourceKey="
         + this.getQuestionResourceKey()
         + ", isValid="
         + this.isValid()
         + ", creationDate="
         + this.getCreationDate()
         + ", numberOfFailedAttempts="
         + this.getNumberOfFailedAttempts()
         + ", isBlocked="
         + this.isBlocked()
         + ", latestSuccessfulUsageDate="
         + this.getLatestSuccessfulUsageDate()
         + ", latestUsageDate="
         + this.getLatestUsageDate()
         + ", answer="
         + Arrays.toString(this.getAnswer())
         + ", normalization="
         + this.getNormalization()
         + ", touchDate="
         + this.getTouchDate()
         + ")";
   }

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

         Object this$questionResourceKey = this.questionResourceKey;
         Object other$questionResourceKey = other.questionResourceKey;
         if (this$questionResourceKey == null ? other$questionResourceKey == null : this$questionResourceKey.equals(other$questionResourceKey)) {
            Object this$creationDate = this.creationDate;
            Object other$creationDate = other.creationDate;
            if (this$creationDate == null ? other$creationDate == null : this$creationDate.equals(other$creationDate)) {
               Object this$normalization = this.normalization;
               Object other$normalization = other.normalization;
               return this$normalization == null ? other$normalization == null : this$normalization.equals(other$normalization);
            } else {
               return false;
            }
         } else {
            return false;
         }
      }
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public String getQuestionResourceKey() {
      return this.questionResourceKey;
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public Date getCreationDate() {
      return this.creationDate;
   }

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

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

   @SuppressFBWarnings(justification = "generated code")
   public byte[] getAnswer() {
      return this.answer;
   }

   @SuppressFBWarnings(justification = "generated code")
   public SecretQuestionsNormalization getNormalization() {
      return this.normalization;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class SecretQuestionBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private String questionResourceKey;
      @SuppressFBWarnings(justification = "generated code")
      private boolean isValid;
      @SuppressFBWarnings(justification = "generated code")
      private Date creationDate;
      @SuppressFBWarnings(justification = "generated code")
      private int numberOfFailedAttempts;
      @SuppressFBWarnings(justification = "generated code")
      private boolean isBlocked;
      @SuppressFBWarnings(justification = "generated code")
      private Date latestSuccessfulUsageDate;
      @SuppressFBWarnings(justification = "generated code")
      private Date latestUsageDate;
      @SuppressFBWarnings(justification = "generated code")
      private byte[] answer;
      @SuppressFBWarnings(justification = "generated code")
      private SecretQuestionsNormalization normalization;
      @SuppressFBWarnings(justification = "generated code")
      private Date touchDate;

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

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder questionResourceKey(String questionResourceKey) {
         this.questionResourceKey = questionResourceKey;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder isValid(boolean isValid) {
         this.isValid = isValid;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder creationDate(Date creationDate) {
         this.creationDate = creationDate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder numberOfFailedAttempts(int numberOfFailedAttempts) {
         this.numberOfFailedAttempts = numberOfFailedAttempts;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder isBlocked(boolean isBlocked) {
         this.isBlocked = isBlocked;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder latestSuccessfulUsageDate(Date latestSuccessfulUsageDate) {
         this.latestSuccessfulUsageDate = latestSuccessfulUsageDate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder latestUsageDate(Date latestUsageDate) {
         this.latestUsageDate = latestUsageDate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder answer(byte[] answer) {
         this.answer = answer;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder normalization(SecretQuestionsNormalization normalization) {
         this.normalization = normalization;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion.SecretQuestionBuilder touchDate(@Nullable Date touchDate) {
         this.touchDate = touchDate;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestion build() {
         return new SecretQuestion(
            this.questionResourceKey,
            this.isValid,
            this.creationDate,
            this.numberOfFailedAttempts,
            this.isBlocked,
            this.latestSuccessfulUsageDate,
            this.latestUsageDate,
            this.answer,
            this.normalization,
            this.touchDate
         );
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "SecretQuestion.SecretQuestionBuilder(questionResourceKey="
            + this.questionResourceKey
            + ", isValid="
            + this.isValid
            + ", creationDate="
            + this.creationDate
            + ", numberOfFailedAttempts="
            + this.numberOfFailedAttempts
            + ", isBlocked="
            + this.isBlocked
            + ", latestSuccessfulUsageDate="
            + this.latestSuccessfulUsageDate
            + ", latestUsageDate="
            + this.latestUsageDate
            + ", answer="
            + Arrays.toString(this.answer)
            + ", normalization="
            + this.normalization
            + ", touchDate="
            + this.touchDate
            + ")";
      }
   }
}
