package com.airlock.iam.admin.application.service.user.rememberme;

import com.airlock.iam.admin.domain.model.rememberme.RememberMeTokenAdminRepository;
import com.airlock.iam.base.api.domain.model.user.UserId;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Arrays;
import java.util.Optional;
import lombok.NonNull;

public final class RememberMeManagementServiceImpl implements RememberMeManagementService {
   private final Optional<RememberMeJspPersisterService> jspTokenPersister;
   private final Optional<RememberMeTokenAdminRepository> flowTokenRepository;
   private final RememberMeAdminLogger logger;

   private RememberMeManagementServiceImpl(
      @NonNull Optional<RememberMeJspPersisterService> jspTokenPersister,
      @NonNull Optional<RememberMeTokenAdminRepository> flowTokenRepository,
      @NonNull RememberMeAdminLogger logger
   ) {
      if (jspTokenPersister == null) {
         throw new NullPointerException("jspTokenPersister is marked non-null but is null");
      }

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

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

      ensureAtLeastOnePresent(jspTokenPersister, flowTokenRepository);
      this.jspTokenPersister = jspTokenPersister;
      this.flowTokenRepository = flowTokenRepository;
      this.logger = logger;
   }

   @Override
   public boolean hasAnyRememberMeTokens(UserId iamUserId) {
      return this.hasJspRememberMeTokens(iamUserId) || this.hasFlowRememberMeTokens(iamUserId);
   }

   private boolean hasJspRememberMeTokens(UserId iamUserId) {
      return this.jspTokenPersister.isPresent() && this.jspTokenPersister.orElseThrow().hasRememberMeCookies(iamUserId);
   }

   private boolean hasFlowRememberMeTokens(UserId iamUserId) {
      return this.flowTokenRepository.isPresent() && this.flowTokenRepository.orElseThrow().hasAnyRememberMeToken(iamUserId);
   }

   @Override
   public void deleteAllRememberMeTokensOf(UserId iamUserId) {
      this.deleteJspRememberMeTokens(iamUserId);
      this.deleteFlowRememberMeTokens(iamUserId);
      this.logger.logRememberMeTokensDeleted(iamUserId);
   }

   private void deleteJspRememberMeTokens(UserId iamUserId) {
      this.jspTokenPersister.ifPresent(p -> p.clearSecretForUser(iamUserId));
   }

   private void deleteFlowRememberMeTokens(UserId iamUserId) {
      this.flowTokenRepository.ifPresent(r -> r.deleteAllRememberMeTokensOf(iamUserId));
   }

   private static void ensureAtLeastOnePresent(Optional<?>... args) {
      if (Arrays.stream(args).allMatch(Optional::isEmpty)) {
         throw new IllegalStateException("Remember-Me feature is not configured!");
      }
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public static class RememberMeManagementServiceImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Optional<RememberMeJspPersisterService> jspTokenPersister;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<RememberMeTokenAdminRepository> flowTokenRepository;
      @SuppressFBWarnings(justification = "generated code")
      private RememberMeAdminLogger logger;

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

      @SuppressFBWarnings(justification = "generated code")
      public RememberMeManagementServiceImpl.RememberMeManagementServiceImplBuilder jspTokenPersister(
         @NonNull Optional<RememberMeJspPersisterService> jspTokenPersister
      ) {
         if (jspTokenPersister == null) {
            throw new NullPointerException("jspTokenPersister is marked non-null but is null");
         }

         this.jspTokenPersister = jspTokenPersister;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RememberMeManagementServiceImpl.RememberMeManagementServiceImplBuilder flowTokenRepository(
         @NonNull Optional<RememberMeTokenAdminRepository> flowTokenRepository
      ) {
         if (flowTokenRepository == null) {
            throw new NullPointerException("flowTokenRepository is marked non-null but is null");
         }

         this.flowTokenRepository = flowTokenRepository;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RememberMeManagementServiceImpl.RememberMeManagementServiceImplBuilder logger(@NonNull RememberMeAdminLogger logger) {
         if (logger == null) {
            throw new NullPointerException("logger is marked non-null but is null");
         }

         this.logger = logger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public RememberMeManagementServiceImpl build() {
         return new RememberMeManagementServiceImpl(this.jspTokenPersister, this.flowTokenRepository, this.logger);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "RememberMeManagementServiceImpl.RememberMeManagementServiceImplBuilder(jspTokenPersister="
            + this.jspTokenPersister
            + ", flowTokenRepository="
            + this.flowTokenRepository
            + ", logger="
            + this.logger
            + ")";
      }
   }
}
