package com.airlock.iam.common.infrastructure.factory;

import com.airlock.iam.base.api.domain.model.exception.ServiceConfigurationException;
import com.airlock.iam.base.api.infrastructure.plugin.PluginConfigToLogic;
import com.airlock.iam.common.api.application.configuration.credential.RequestAuthenticationConfig;
import com.airlock.iam.common.api.domain.model.credential.RequestAuthentication;
import com.airlock.iam.common.api.domain.service.credential.RequestAuthenticationService;
import com.airlock.iam.common.application.configuration.RestConfigSpec;
import com.airlock.iam.common.application.configuration.credential.BaseRequestAuthenticationConfig;
import com.airlock.iam.common.domain.model.user.Lockable;
import com.airlock.iam.common.domain.model.user.LockableImpl;
import com.airlock.iam.common.domain.model.user.TentativePrincipalInfo;
import com.airlock.iam.common.domain.service.adapt.PersistentUserAdapter;
import com.airlock.iam.common.domain.service.credential.RequestAuthenticationLockService;
import com.airlock.iam.common.domain.service.credential.RequestAuthenticationLockServiceImpl;
import com.airlock.iam.common.domain.service.credential.RequestAuthenticationServiceImpl;
import com.airlock.iam.common.domain.service.credential.RequestAuthenticationSettings;
import com.airlock.iam.common.domain.service.user.UserLockService;
import com.airlock.iam.common.domain.service.user.UserLockServiceImpl;
import com.airlock.iam.common.infrastructure.adapt.PersistentUserAdapterImpl;
import com.google.inject.Exposed;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.servlet.RequestScoped;
import jakarta.inject.Provider;
import java.util.Collections;
import java.util.Optional;
import java.util.OptionalInt;

public final class RequestAuthenticationServiceModule extends PrivateModule {
   @Provides
   RequestAuthenticationConfig provideRequestAuthenticationConfig(RestConfigSpec config) {
      return ServiceConfigurationException.checkPresent(config.getRequestAuthenticationConfig(), "'Request Authentication' not configured.");
   }

   @Provides
   RequestAuthentication provideRequestAuthentication(PluginConfigToLogic configToLogic, RequestAuthenticationConfig config) {
      return configToLogic.getLogic(config);
   }

   @Provides
   @Exposed
   RequestAuthenticationSettings provideRequestAuthenticationSettings(RequestAuthenticationConfig config) {
      return config instanceof BaseRequestAuthenticationConfig baseConfig
         ? new RequestAuthenticationSettings(
            baseConfig.getUserStore(), baseConfig.getStaticRoles(), baseConfig.getRolesBlockList(), baseConfig.getMaxFailedAttempts()
         )
         : new RequestAuthenticationSettings(Optional.empty(), Collections.emptySet(), Collections.emptySet(), OptionalInt.empty());
   }

   @Exposed
   @Provides
   Optional<RequestAuthenticationService> provideRequestAuthenticationService(Provider<RequestAuthenticationService> service, RestConfigSpec config) {
      return config.getRequestAuthenticationConfig().isEmpty() ? Optional.empty() : Optional.of((RequestAuthenticationService)service.get());
   }

   @Provides
   PersistentUserAdapter<Lockable> provideAdapter() {
      return PersistentUserAdapterImpl.create(LockableImpl.class);
   }

   @Provides
   RequestAuthenticationLockService provideRequestAuthenticationLockService(RequestAuthenticationConfig config, UserLockService userLockService) {
      if (config instanceof BaseRequestAuthenticationConfig baseConfig) {
         return new RequestAuthenticationLockServiceImpl(((UserLockServiceImpl)userLockService).withDedicatedUserStore(baseConfig.getUserStore().orElseThrow()));
      } else {
         throw new ServiceConfigurationException("Unsupported Request Authentication.");
      }
   }

   @Provides
   @RequestScoped
   @Exposed
   TentativePrincipalInfo provideTentativePrincipalInfo() {
      return new TentativePrincipalInfo();
   }

   protected void configure() {
      this.bind(RequestAuthenticationService.class).to(RequestAuthenticationServiceImpl.class);
      this.expose(RequestAuthenticationService.class);
   }
}
