package com.airlock.iam.common.domain.service.secretquestions;

import com.airlock.iam.common.api.domain.model.store.user.UserStore;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.common.application.configuration.secretquestion.GeneralSecretQuestionsSettings;
import com.airlock.iam.common.domain.model.token.secretquestions.SecretQuestion;
import com.airlock.iam.common.domain.model.token.secretquestions.SecretQuestionAnswer;
import com.airlock.iam.common.domain.model.token.secretquestions.SecretQuestionsRepository;
import com.airlock.iam.common.domain.service.exception.FeatureDisabledException;
import com.airlock.iam.log.api.signing.SigningLogger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import java.util.List;
import java.util.Optional;

public class SecretQuestionsService {
   private final UserStore userStore;
   private final GeneralSecretQuestionsSettings secretQuestionsSettings;
   private final SecretQuestionsRepository secretQuestionsRepository;
   private final SigningLogger signingLogger;

   @Inject
   public SecretQuestionsService(UserStore userStore, GeneralSecretQuestionsSettings settings, SecretQuestionsRepository tokenRepository, SigningLogger logger) {
      this.userStore = userStore;
      this.secretQuestionsSettings = settings;
      this.secretQuestionsRepository = tokenRepository;
      this.signingLogger = logger;
   }

   public boolean isSecretQuestionsEnabled(String userId) {
      Preconditions.checkNotNull(userId);
      PersistentUser user = this.userStore.getByIdentity(userId);
      return user.isSecretQuestionsEnabled();
   }

   public boolean isProvisioningRequired(String userId) {
      return this.isSecretQuestionsEnabled(userId) && this.getNumberOfMissingAnswers(userId) > 0;
   }

   private List<SecretQuestion> getQuestionsForChallengeInternal(String userId) {
      return this.secretQuestionsRepository.query().forUser(userId).withoutRemovedQuestions().withoutBlockedAnswers().getResults();
   }

   public int getNumberOfMissingAnswers(String userId) {
      Preconditions.checkNotNull(userId);
      this.checkSecretQuestionsEnabled(userId);
      int numberOfAnsweredQuestions = this.getQuestionsForChallengeInternal(userId).size();
      int numberOfMissingAnswers = this.secretQuestionsSettings.getRequiredNumberOfProvisionedAnswers() - numberOfAnsweredQuestions;
      return Math.max(numberOfMissingAnswers, 0);
   }

   public boolean verifyAnswer(SecretQuestionAnswer answer) {
      Preconditions.checkNotNull(answer);
      Preconditions.checkNotNull(answer.getUserId());
      this.checkSecretQuestionsEnabled(answer.getUserId());
      Optional<SecretQuestion> secretQuestionOptional = this.secretQuestionsRepository
         .query()
         .forUser(answer.getUserId())
         .withQuestionResourceKey(answer.getQuestionResourceKey())
         .withoutBlockedAnswers()
         .withoutRemovedQuestions()
         .getResult();
      if (secretQuestionOptional.isEmpty()) {
         this.signingLogger.log("could not verify answer for user \"%s\"", new Object[]{answer.getUserId()});
         return false;
      }

      SecretQuestion secretQuestion = secretQuestionOptional.get().touchLatestUsageDate();
      boolean isAnswerCorrect = secretQuestion.isAnsweredBy(
         answer, this.secretQuestionsSettings.isCheckUsingLatin1Encoding(), this.secretQuestionsSettings.getHashFunctionPlugin()
      );
      if (isAnswerCorrect) {
         secretQuestion = secretQuestion.withoutFailedAttempts()
            .touchSuccessfulUsageDate()
            .updateAnswerHash(answer, this.secretQuestionsSettings.getHashFunctionPlugin());
      } else {
         secretQuestion = secretQuestion.incrementFailedAttempts();
      }

      this.secretQuestionsRepository.update(answer.getUserId(), secretQuestion);
      this.signingLogger.log("verified answer for user \"%s\"", new Object[]{answer.getUserId()});
      return isAnswerCorrect;
   }

   @VisibleForTesting
   boolean isForbiddenDuplicate(String userId, String newAnswer) {
      if (!this.secretQuestionsSettings.areDuplicateAnswersForbidden()) {
         return false;
      }

      for (SecretQuestion existingSecretQuestion : this.secretQuestionsRepository.query().forUser(userId).getResults()) {
         if (existingSecretQuestion.isEqualAnswer(
            newAnswer, this.secretQuestionsSettings.isCheckUsingLatin1Encoding(), this.secretQuestionsSettings.getHashFunctionPlugin()
         )) {
            return true;
         }
      }

      return false;
   }

   private void checkSecretQuestionsEnabled(String userId) {
      boolean enabled = this.isSecretQuestionsEnabled(userId);
      FeatureDisabledException.checkTrue(enabled, userId, "Secret Questions are not enabled for user '" + userId + "'");
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class SecretQuestionsServiceBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private UserStore userStore;
      @SuppressFBWarnings(justification = "generated code")
      private GeneralSecretQuestionsSettings secretQuestionsSettings;
      @SuppressFBWarnings(justification = "generated code")
      private SecretQuestionsRepository secretQuestionsRepository;
      @SuppressFBWarnings(justification = "generated code")
      private SigningLogger signingLogger;

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

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsService.SecretQuestionsServiceBuilder userStore(UserStore userStore) {
         this.userStore = userStore;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsService.SecretQuestionsServiceBuilder secretQuestionsSettings(GeneralSecretQuestionsSettings secretQuestionsSettings) {
         this.secretQuestionsSettings = secretQuestionsSettings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsService.SecretQuestionsServiceBuilder secretQuestionsRepository(SecretQuestionsRepository secretQuestionsRepository) {
         this.secretQuestionsRepository = secretQuestionsRepository;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsService.SecretQuestionsServiceBuilder signingLogger(SigningLogger signingLogger) {
         this.signingLogger = signingLogger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public SecretQuestionsService build() {
         return new SecretQuestionsService(this.userStore, this.secretQuestionsSettings, this.secretQuestionsRepository, this.signingLogger);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "SecretQuestionsService.SecretQuestionsServiceBuilder(userStore="
            + this.userStore
            + ", secretQuestionsSettings="
            + this.secretQuestionsSettings
            + ", secretQuestionsRepository="
            + this.secretQuestionsRepository
            + ", signingLogger="
            + this.signingLogger
            + ")";
      }
   }
}
