package com.airlock.iam.authentication.domain.model.secretquestions;

import com.airlock.iam.authentication.application.configuration.secretquestions.SecretQuestionsProvisioningStepConfig;
import com.airlock.iam.authentication.domain.service.secretquestions.SecretQuestionsLoginService;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.flow.api.domain.model.step.GenericStepError;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.api.domain.model.step.StepResultBuilder;
import com.airlock.iam.flow.domain.model.ValidationStepResultData;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@LogicForConfig(SecretQuestionsProvisioningStepConfig.class)
public final class SecretQuestionsProvisioningStepImpl implements SecretQuestionsProvisioningStep {
   private static final String MISSING_SECRET_QUESTION_ANSWERS_META_DATA_KEY = "missingSecretQuestionAnswers";
   private static final String SECRET_QUESTIONS_STATUS_ATTRIBUTE = "secretQuestionsInfo";
   private static final String NUM_MISSING_ATTRIBUTE = "missingAnswers";
   private static final String QUESTIONS_ATTRIBUTE = "questions";
   private static final String ID_ATTRIBUTE = "id";
   private static final String PROVISIONED_ATTRIBUTE = "provisioned";
   private final Provider<PrincipalInfo> user;
   private final Provider<SecretQuestionsLoginService> service;
   private final HelpdeskLogger helpdeskLogger;

   @Inject
   public SecretQuestionsProvisioningStepImpl(Provider<PrincipalInfo> user, Provider<SecretQuestionsLoginService> service, HelpdeskLogger helpdeskLogger) {
      this.user = user;
      this.service = service;
      this.helpdeskLogger = helpdeskLogger;
   }

   @Override
   public StepResult initialize() {
      return this.isProvisioningRequired()
         ? this.addStatus(this.stepResultFactory().stay(SecretQuestionsProvisioningStep.SecretQuestionsStepAction.SECRET_QUESTIONS_PROVISIONING_REQUIRED))
            .build()
         : this.stepResultFactory().skip().build();
   }

   @Override
   public String getDescription() {
      return "Secret Questions Provisioning Step";
   }

   @Override
   public StepResult retrieve() {
      SecretQuestionStatusList status = ((SecretQuestionsLoginService)this.service.get()).status(this.userId());
      return this.stepResultFactory()
         .stay(SecretQuestionsProvisioningStep.SecretQuestionsStepAction.SECRET_QUESTIONS_PROVISIONING_REQUIRED)
         .resultData(status)
         .addMetadataItem("missingSecretQuestionAnswers", status.getMissingAnswers())
         .build();
   }

   @Override
   public StepResult provision(SecretQuestionsProvision provisions) {
      ValidationResult validationResult = ((SecretQuestionsLoginService)this.service.get()).register(this.userId(), provisions);
      if (!validationResult.isValid()) {
         this.helpdeskLogger.log(this.userId(), "One or several answers to secret questions are invalid. No answers have been provisioned.");
         return this.addStatus(
               this.stepResultFactory()
                  .failRetry(
                     GenericStepError.VALIDATION_FAILED, SecretQuestionsProvisioningStep.SecretQuestionsStepAction.SECRET_QUESTIONS_PROVISIONING_REQUIRED
                  )
            )
            .resultData(new ValidationStepResultData(validationResult))
            .build();
      }

      if (this.isProvisioningRequired()) {
         return this.addStatus(this.stepResultFactory().stay(SecretQuestionsProvisioningStep.SecretQuestionsStepAction.SECRET_QUESTIONS_PROVISIONING_REQUIRED))
            .build();
      }

      this.helpdeskLogger.log(this.userId(), "Successfully provisioned secret questions");
      return this.stepResultFactory().success().build();
   }

   private StepResultBuilder addStatus(StepResultBuilder builder) {
      SecretQuestionStatusList status = ((SecretQuestionsLoginService)this.service.get()).status(this.userId());
      Map<String, Object> map = new HashMap<>();
      map.put("missingAnswers", status.getMissingAnswers());
      List<Map<String, Object>> questions = new ArrayList<>();
      status.getSecretQuestions().forEach(q -> questions.add(Map.of("id", q.getId(), "provisioned", q.isAnswerProvisioned())));
      map.put("questions", questions);
      builder.addAdditionalAttribute("secretQuestionsInfo", map);
      return builder;
   }

   private boolean isProvisioningRequired() {
      return ((SecretQuestionsLoginService)this.service.get()).isProvisioningRequired(this.userId());
   }

   private String userId() {
      return ((PrincipalInfo)this.user.get())
         .getKnownIdentity()
         .map(identity -> identity.id().getValue())
         .orElseThrow(() -> new FailureException("User must be known"));
   }
}
