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

import com.airlock.iam.common.api.domain.model.user.User;
import com.airlock.iam.common.api.domain.model.user.UsernameToPropagate;
import com.airlock.iam.common.application.configuration.RestConfigSpec;
import com.airlock.iam.common.application.configuration.gateway.AirlockMicrogatewayConfig;
import com.airlock.iam.common.application.configuration.language.LanguageSettings;
import com.airlock.iam.common.application.configuration.logging.CorrelationIdSettingsConfig;
import com.airlock.iam.common.application.configuration.templock.TemporaryLockingConfig;
import com.airlock.iam.common.application.locale.LocalePreferenceConfiguration;
import com.airlock.iam.common.application.service.DefaultSessionTerminationService;
import com.airlock.iam.common.application.service.session.SessionTerminationService;
import com.airlock.iam.common.domain.model.principal.UserPrincipal;
import com.airlock.iam.common.domain.service.SessionService;
import com.airlock.iam.common.domain.service.username.UsernameFilterSettings;
import com.airlock.iam.common.domain.service.username.UsernameValidationService;
import com.airlock.iam.common.domain.service.username.UsernameValidationServiceImpl;
import com.airlock.iam.common.infrastructure.delay.ResponseDelayCondition;
import com.airlock.iam.common.infrastructure.delay.ResponseDelayService;
import com.airlock.iam.common.infrastructure.delay.ResponseDelayServiceImpl;
import com.airlock.iam.common.infrastructure.delay.ResponseDelaySettings;
import com.airlock.iam.common.infrastructure.filter.security.SecuritySettings;
import com.airlock.iam.common.infrastructure.session.SessionServiceImpl;
import com.airlock.iam.core.application.configuration.waf.AirlockGatewayConfig;
import com.airlock.iam.core.misc.authen.UsernameTransformationManager;
import com.airlock.iam.core.misc.authorization.AccessController;
import com.airlock.iam.flow.application.service.GenericFlowSelectionService;
import com.airlock.iam.flow.infrastructure.mapper.DefaultFlowResponseTransformer;
import com.airlock.iam.flow.infrastructure.transfer.response.FlowResponseTransformer;
import com.airlock.iam.flow.shared.domain.service.MetadataProvider;
import com.airlock.iam.flow.shared.infrastructure.service.FailureResponseDelayCondition;
import com.airlock.iam.flow.shared.infrastructure.service.NoMetadataProvider;
import com.airlock.iam.transactionapproval.application.configuration.TransactionApprovalApp;
import com.google.inject.Exposed;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.servlet.RequestScoped;
import java.time.Duration;
import java.util.Optional;
import java.util.regex.Pattern;

public final class TransactionApprovalInfrastructureModule extends PrivateModule {
   @Exposed
   @Provides
   LanguageSettings provideLanguageSettings(TransactionApprovalApp config) {
      return config.getLanguageSettings();
   }

   @Exposed
   @Provides
   @RequestScoped
   Optional<AirlockGatewayConfig> provideAirlockGatewaySettings(TransactionApprovalApp config) {
      return config.getGatewayConfig();
   }

   @Exposed
   @Provides
   @RequestScoped
   Optional<AirlockMicrogatewayConfig> provideMicrogatewayConfig(TransactionApprovalApp config) {
      return config.getMicrogatewayConfig();
   }

   @Exposed
   @Provides
   @RequestScoped
   SecuritySettings provideSecuritySettings(TransactionApprovalApp config) {
      return new SecuritySettings(config.getCorsSettings(), config.isCsrfProtected(), Duration.ZERO);
   }

   @Provides
   @RequestScoped
   UsernameFilterSettings provideUsernameFilterSettings() {
      return new UsernameFilterSettings(Pattern.compile(".*"));
   }

   @Exposed
   @Provides
   LocalePreferenceConfiguration provideLocalePreferenceConfiguration(LanguageSettings languageSettings) {
      return new LocalePreferenceConfiguration(languageSettings.getDefaultLanguage(), languageSettings.getValidLanguages());
   }

   @Exposed
   @Provides
   RestConfigSpec provideRestConfigSpec(TransactionApprovalApp config) {
      return config;
   }

   @Exposed
   @Provides
   UsernameTransformationManager provideUsernameTransformationManager(RestConfigSpec config) {
      return new UsernameTransformationManager(config.getUsernameTransformers());
   }

   @Exposed
   @Provides
   @RequestScoped
   Optional<CorrelationIdSettingsConfig> provideCorrelationIdSettingsConfig(TransactionApprovalApp config) {
      return config.getCorrelationIdSettingsConfig();
   }

   @Provides
   @RequestScoped
   @Exposed
   ResponseDelayService provideResponseDelayTiming(TransactionApprovalApp config, ResponseDelayCondition condition) {
      return new ResponseDelayServiceImpl(new ResponseDelaySettings(Duration.ofMillis(config.getFixedResponseDuration()), condition));
   }

   protected void configure() {
      this.bind(ResponseDelayCondition.class).to(FailureResponseDelayCondition.class).in(RequestScoped.class);
      this.bind(FlowResponseTransformer.class).to(DefaultFlowResponseTransformer.class);
      this.expose(FlowResponseTransformer.class);
      this.bind(SessionTerminationService.class).to(DefaultSessionTerminationService.class);
      this.expose(SessionTerminationService.class);
      this.bind(GenericFlowSelectionService.class).in(RequestScoped.class);
      this.expose(GenericFlowSelectionService.class);
      this.bind(UsernameValidationService.class).to(UsernameValidationServiceImpl.class).in(RequestScoped.class);
      this.expose(UsernameValidationService.class);
      this.bind(MetadataProvider.class).toInstance(new NoMetadataProvider());
      this.expose(MetadataProvider.class);
      this.bind(new TypeLiteral<Optional<AccessController>>() {}).toInstance(Optional.empty());
      this.expose(new TypeLiteral<Optional<AccessController>>() {});
      this.bind(new TypeLiteral<Optional<TemporaryLockingConfig>>() {}).toInstance(Optional.empty());
      this.expose(new TypeLiteral<Optional<TemporaryLockingConfig>>() {});
      this.bind(new TypeLiteral<Optional<UsernameToPropagate>>() {}).toInstance(Optional.empty());
      this.expose(new TypeLiteral<Optional<UsernameToPropagate>>() {});
      this.bind(User.class).to(UserPrincipal.class).in(RequestScoped.class);
      this.expose(User.class);
      this.bind(SessionService.class).to(SessionServiceImpl.class);
      this.expose(SessionService.class);
   }
}
