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

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.user.UserIdentity;
import com.airlock.iam.common.domain.model.user.PrincipalInfo;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.factor.domain.model.fido.FidoUserCredential;
import com.airlock.iam.factor.domain.model.fido.FidoUserCredentialRepository;
import com.airlock.iam.flow.shared.api.domain.model.session.ChangeDescriptionFlowAttribute;
import com.airlock.iam.flow.shared.application.configuration.step.ApplyFidoCredentialDisplayNameChangeConfig;
import com.airlock.iam.flow.shared.domain.model.step.apply.ApplyChangeHandler;
import com.airlock.iam.log.api.helpdesk.HelpdeskLogger;
import com.airlock.iam.log.api.signing.SigningLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.Strings;

@LogicForConfig(ApplyFidoCredentialDisplayNameChangeConfig.class)
public final class ApplyFidoCredentialDisplayNameChange implements ApplyChangeHandler<FidoCredentialDisplayNameChangeFlowAttribute> {
   private static final ELogger LOG = ELoggerFactory.getLogger(ApplyFidoCredentialDisplayNameChange.class);
   private final Provider<PrincipalInfo> user;
   private final Provider<FidoUserCredentialRepository> repository;
   private final SigningLogger signingLogger;
   private final HelpdeskLogger helpdeskLogger;

   @Inject
   public ApplyFidoCredentialDisplayNameChange(
      Provider<PrincipalInfo> user, Provider<FidoUserCredentialRepository> repository, SigningLogger signingLogger, HelpdeskLogger helpdeskLogger
   ) {
      this.user = user;
      this.repository = repository;
      this.signingLogger = signingLogger;
      this.helpdeskLogger = helpdeskLogger;
   }

   public Map<String, Object> applyChange(FidoCredentialDisplayNameChangeFlowAttribute change) {
      Optional<FidoUserCredential> fidoUserCredential = ((FidoUserCredentialRepository)this.repository.get()).findById(change.getCredentialId(), this.userId());
      if (fidoUserCredential.isEmpty()) {
         LOG.debug("FIDO credential no longer exists - skipping");
         return Map.of();
      } else if (Strings.CS.equals(fidoUserCredential.get().getDisplayName().getName(), change.getNewName())) {
         LOG.debug("New FIDO credential display name is the same as the existing name - skipping");
         return Map.of();
      } else {
         fidoUserCredential.get().setDisplayName(change.getNewName());
         ((FidoUserCredentialRepository)this.repository.get()).update(fidoUserCredential.get());
         this.signingLogger
            .log(
               "User "
                  + LogUtil.quote(this.userId())
                  + " changed display name of FIDO credential with ID "
                  + LogUtil.quote(change.getCredentialId())
                  + " to "
                  + LogUtil.logSafeQuote(change.getNewName())
            );
         this.helpdeskLogger
            .log(
               this.userId(),
               "Changed display name of FIDO credential with ID "
                  + LogUtil.quote(change.getCredentialId())
                  + " to "
                  + LogUtil.logSafeQuote(change.getNewName())
            );
         return Map.of();
      }
   }

   @Override
   public <S extends ChangeDescriptionFlowAttribute<S>> boolean handlesChangeOfType(Class<S> type) {
      return type.isAssignableFrom(FidoCredentialDisplayNameChangeFlowAttribute.class);
   }

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