package com.airlock.iam.flow.shared.domain.model.rememberme;

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.user.UserId;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.persistency.exception.PersisterException;
import com.airlock.iam.common.api.domain.model.user.UserIdentity;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.core.misc.impl.persistency.CredentialBeanImpl;
import com.airlock.iam.core.misc.persistency.CredentialBean;
import com.airlock.iam.core.misc.persistency.CredentialPersister;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.step.FlowStep;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.shared.application.configuration.rememberme.RememberMeResetStepConfig;
import com.airlock.iam.log.api.signing.SigningLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;

@LogicForConfig(RememberMeResetStepConfig.class)
public final class RememberMeResetStep implements FlowStep {
   private static final ELogger LOG = ELoggerFactory.getLogger(RememberMeResetStep.class);
   private final Provider<PrincipalInfo> user;
   private final Provider<RememberMeTokenRepository> repository;
   private final Provider<Optional<CredentialPersister>> jspPersister;
   private final SigningLogger signingLogger;

   @Inject
   public RememberMeResetStep(
      Provider<PrincipalInfo> user,
      Provider<RememberMeTokenRepository> repository,
      Provider<Optional<CredentialPersister>> jspPersister,
      SigningLogger signingLogger
   ) {
      this.user = user;
      this.repository = repository;
      this.jspPersister = jspPersister;
      this.signingLogger = signingLogger;
   }

   @Override
   public StepResult initialize() {
      UserId userId = ((PrincipalInfo)this.user.get())
         .getKnownIdentity()
         .map(UserIdentity::id)
         .orElseThrow(() -> new FailureException("User must be known to reset Remember-Me"));
      int flowSecrets = this.deleteFlowSecrets(userId);
      int jspSecrets = this.deleteJspSecrets(userId);
      if (flowSecrets + jspSecrets > 0) {
         this.signingLogger.log("Deleted all Remember-Me secrets for user " + LogUtil.quote(userId));
      } else {
         LOG.debug("No Remember-Me secrets deleted for user " + LogUtil.quote(userId));
      }

      return this.stepResultFactory().success().build();
   }

   private int deleteFlowSecrets(UserId userId) {
      RememberMeTokenRepository repo = (RememberMeTokenRepository)this.repository.get();
      List<RememberMeToken> tokens = repo.findBy(userId);
      tokens.forEach(token -> repo.deleteBy(token.getId()));
      return tokens.size();
   }

   private int deleteJspSecrets(UserId userId) {
      if (((Optional)this.jspPersister.get()).isEmpty()) {
         return 0;
      }

      CredentialPersister persister = (CredentialPersister)((Optional)this.jspPersister.get()).get();

      try {
         CredentialBean credential = persister.getPersistentCredentialByUsername(userId.getValue());
         if (hasSecret(credential)) {
            persister.makeCredentialPersistent(userId.getValue(), new CredentialBeanImpl(CredentialBean.CredentialDataEncoding.STRING));
            return 1;
         } else {
            return 0;
         }
      } catch (PersisterException e) {
         throw new FailureException(e);
      }
   }

   private static boolean hasSecret(CredentialBean credential) {
      return credential != null && credential.getCurrent() != null && !StringUtils.isBlank(credential.getCurrent().getStringData());
   }

   @Override
   public String getDescription() {
      return "Remember-Me Reset Step";
   }
}
