package com.airlock.iam.admin.infrastructure.factory.prot;

import com.airlock.iam.admin.api.domain.service.user.ManagedUserRepository;
import com.airlock.iam.admin.application.configuration.generic.GenericTokenControllerConfig;
import com.airlock.iam.admin.application.configuration.users.UsersConfiguration;
import com.airlock.iam.admin.application.service.authmethod.AuthenticationMethodService;
import com.airlock.iam.admin.application.service.authmethod.AuthenticationMethodSettings;
import com.airlock.iam.admin.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.admin.domain.model.token.TokenController;
import com.airlock.iam.admin.domain.model.user.UserSpecificSettings;
import com.airlock.iam.admin.infrastructure.factory.TokenServiceFactory;
import com.airlock.iam.base.api.domain.model.exception.ServiceConfigurationException;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.name.Names;
import com.google.inject.servlet.RequestScoped;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;

public final class AuthenticationMethodServiceModule extends PrivateModule {
   @Provides
   AuthenticationMethodSettings provideAuthenticationMethodSettings(Optional<UsersConfiguration> usersConfiguration) {
      UsersConfiguration usersConfig = (UsersConfiguration)ServiceConfigurationException.checkPresent(usersConfiguration, "users are not configured");
      return new AuthenticationMethodSettings(usersConfig.getAdditionalAuthMethodProperties(), usersConfig.isEnableMultipleNextAuthMethods());
   }

   @Provides
   List<AuthenticationMethod> provideAuthenticationMethods(
      UserSpecificSettings userSpecificSettings, ManagedUserRepository userRepository, Injector injector, Provider<TokenServiceFactory> tokenServiceFactory
   ) {
      return userSpecificSettings.getTokenControllers()
         .stream()
         .map(controller -> authMethodOf(controller, userRepository, injector, tokenServiceFactory))
         .toList();
   }

   private static AuthenticationMethod authMethodOf(
      TokenController controller, ManagedUserRepository userRepository, Injector injector, Provider<TokenServiceFactory> tokenServiceFactory
   ) {
      if (GenericTokenControllerConfig.class.isAssignableFrom(controller.getClass())) {
         GenericTokenControllerConfig config = (GenericTokenControllerConfig)controller;
         return config.authenticationMethod(userRepository, tokenServiceFactory);
      } else {
         return (AuthenticationMethod)injector.getInstance(Key.get(AuthenticationMethod.class, Names.named(controller.getTokenControllerId())));
      }
   }

   protected void configure() {
      this.bind(AuthenticationMethodService.class).in(RequestScoped.class);
      this.expose(AuthenticationMethodService.class);
   }
}
