package com.airlock.iam.admin.application.service.token.fido;

import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.factor.domain.model.fido.FidoEventFactory;
import com.airlock.iam.factor.domain.model.fido.FidoUserCredential;
import com.airlock.iam.factor.domain.model.fido.FidoUserCredentialRepository;
import com.airlock.iam.fido.api.domain.model.FidoCredentialId;
import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import lombok.NonNull;

public final class FidoUserCredentialAdminServiceImpl implements FidoUserCredentialAdminService {
   private static final Comparator<FidoUserCredential> LAST_USED_COMPARATOR = Comparator.comparing(
      FidoUserCredential::lastUsedAt, Comparator.comparing(l -> l.orElse(Instant.MIN))
   );
   private static final Comparator<FidoUserCredential> REGISTERED_AT_COMPARATOR = Comparator.comparing(FidoUserCredential::getRegisteredAt);
   private final FidoUserCredentialRepository repository;
   private final Provider<FidoEventFactory> eventFactory;
   private final FidoAdminLogger logger;

   @Inject
   FidoUserCredentialAdminServiceImpl(
      @Nonnull FidoUserCredentialRepository repository, @NonNull Provider<FidoEventFactory> eventFactory, @NonNull FidoAdminLogger logger
   ) {
      if (eventFactory == null) {
         throw new NullPointerException("eventFactory is marked non-null but is null");
      }

      if (logger == null) {
         throw new NullPointerException("logger is marked non-null but is null");
      }

      this.repository = repository;
      this.eventFactory = eventFactory;
      this.logger = logger;
   }

   @Override
   public List<FidoUserCredential> getCredentialsForUser(UserId userId) {
      return this.repository.findBy(userId).stream().sorted(LAST_USED_COMPARATOR.thenComparing(REGISTERED_AT_COMPARATOR).reversed()).toList();
   }

   @Override
   public boolean deleteCredential(FidoCredentialId credentialId, UserId userId) {
      if (this.repository.delete(credentialId, userId)) {
         ((FidoEventFactory)this.eventFactory.get()).publishFidoCredentialDeletedEvent(credentialId, userId);
         this.logger.logCredentialDeleted(credentialId, userId);
         return true;
      } else {
         return false;
      }
   }

   @Override
   public FidoUserCredentialLockChangeResult lock(FidoCredentialId credentialId, UserId userId) {
      Optional<FidoUserCredential> credential = this.repository.findById(credentialId, userId);
      if (credential.isPresent()) {
         if (credential.get().lock()) {
            this.repository.update(credential.get());
            this.logger.logCredentialLocked(credential.get(), userId);
            return FidoUserCredentialLockChangeResult.SUCCESS;
         } else {
            return FidoUserCredentialLockChangeResult.NOT_UPDATED;
         }
      } else {
         return FidoUserCredentialLockChangeResult.NOT_FOUND;
      }
   }

   @Override
   public FidoUserCredentialLockChangeResult unlock(FidoCredentialId credentialId, UserId userId) {
      Optional<FidoUserCredential> credential = this.repository.findById(credentialId, userId);
      if (credential.isPresent()) {
         if (credential.get().unlock()) {
            this.repository.update(credential.get());
            this.logger.logCredentialUnlocked(credential.get(), userId);
            return FidoUserCredentialLockChangeResult.SUCCESS;
         } else {
            return FidoUserCredentialLockChangeResult.NOT_UPDATED;
         }
      } else {
         return FidoUserCredentialLockChangeResult.NOT_FOUND;
      }
   }
}
