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

import com.airlock.iam.authentication.application.configuration.targetapp.TargetApplicationConfigSpec;
import com.airlock.iam.authentication.application.configuration.targetapp.TargetApplicationsConfig;
import com.airlock.iam.authentication.application.configuration.ui.AuthenticationUiConfig;
import com.airlock.iam.authentication.domain.model.idpropagation.CurrentTargetApplication;
import com.airlock.iam.authentication.domain.model.idpropagation.FlowIdentityPropagator;
import com.airlock.iam.authentication.domain.model.idpropagation.TargetURIIdentityPropagator;
import com.airlock.iam.authentication.domain.model.idpropagation.TargetUriToPropagate;
import com.airlock.iam.authentication.domain.model.session.CurrentApplicationId;
import com.airlock.iam.authentication.infrastructure.service.oneshot.OneShotResponseService;
import com.airlock.iam.authentication.infrastructure.service.ui.AuthenticationUiService;
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.domain.model.user.UsernameToPropagate;
import com.airlock.iam.common.api.domain.model.valueprovider.ValueProvider;
import com.airlock.iam.flow.api.domain.model.flow.ApplicationId;
import com.airlock.iam.login.domain.model.location.propagate.TargetURIResolver;
import com.airlock.iam.login.domain.model.targetapp.WafCredentialProvider;
import com.google.inject.Exposed;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.servlet.RequestScoped;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public final class CurrentTargetApplicationModule extends PrivateModule {
   @Provides
   ApplicationId provideApplicationId(TargetApplicationConfigSpec targetApplicationConfig) {
      return targetApplicationConfig.getApplicationId();
   }

   @Provides
   @Exposed
   @RequestScoped
   Optional<UsernameToPropagate> provideUsernameToPropagate(TargetApplicationConfigSpec application, PluginConfigToLogic toLogic) {
      ValueProvider<String> usernameProvider = toLogic.getLogic(application.getUsernameToPropagateProvider());
      return Optional.ofNullable(usernameProvider.getValue()).map(UsernameToPropagate::usernameToPropagateOf);
   }

   @Provides
   TargetApplicationConfigSpec currentApplicationConfig(TargetApplicationsConfig config, Optional<CurrentApplicationId> applicationId) {
      return applicationId.map(CurrentApplicationId::applicationId)
         .flatMap(config::getApplicationConfig)
         .orElseThrow(() -> new ServiceConfigurationException("Cannot find config for current application"));
   }

   @Provides
   TargetApplicationsConfig provideTargetApplicationsConfig(Optional<TargetApplicationsConfig> optionalRestAuthenticationConfig) {
      return ServiceConfigurationException.checkPresent(optionalRestAuthenticationConfig, "The authentication API must be configured");
   }

   @Provides
   List<WafCredentialProvider> wafCredentialProviders(TargetApplicationConfigSpec currentTargetApplicationConfig, PluginConfigToLogic toLogic) {
      return currentTargetApplicationConfig.getAirlockGatewayRoles()
         .stream()
         .map(wafCredentialProviderConfig -> toLogic.getLogic(wafCredentialProviderConfig))
         .toList();
   }

   @Provides
   List<FlowIdentityPropagator> provideIdentityPropagators(
      TargetApplicationConfigSpec currentTargetApplicationConfig, Optional<TargetURIIdentityPropagator> forwardUrlPropagator, PluginConfigToLogic toLogic
   ) {
      List<FlowIdentityPropagator> logics = currentTargetApplicationConfig.getIdentityPropagation().stream().map(idp -> toLogic.getLogic(idp)).toList();
      return forwardUrlPropagator.isEmpty() ? logics : Stream.concat(logics.stream(), Stream.of(forwardUrlPropagator.get())).toList();
   }

   @Provides
   Optional<TargetURIResolver> provideOnFlowCompletionTargetURIResolver(PluginConfigToLogic toLogic, AuthenticationUiService uiService) {
      return uiService.getCurrentApplicationUiConfig().map(AuthenticationUiConfig::getTargetURIResolver).map(toLogic::getLogic);
   }

   @Provides
   Optional<TargetURIIdentityPropagator> provideForwardUrlPropagator(ForwardUrlIdPropagatorFactory factory) {
      return factory.createForwardUrlIdPropagator();
   }

   @Provides
   @Exposed
   @RequestScoped
   OneShotResponseService provideOneShotResponseService(TargetApplicationConfigSpec config, OneShotResponseServiceFactory factory) {
      return factory.createService(config);
   }

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