package com.airlock.iam.publicselfservice.infrastructure.factory.shared;

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
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.templock.TemporaryLockingConfig;
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.infrastructure.delay.ResponseDelayCondition;
import com.airlock.iam.core.misc.authorization.AccessController;
import com.airlock.iam.flow.application.configuration.FlowConfig;
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.ui.application.configuration.FlowUiConfig;
import com.airlock.iam.flow.ui.infrastructure.service.ui.DefaultStepUiConfigRepository;
import com.airlock.iam.publicselfservice.application.configuration.flow.PublicSelfServiceFlowConfig;
import com.airlock.iam.publicselfservice.application.configuration.ui.PublicSelfServiceUiConfig;
import com.airlock.iam.publicselfservice.application.configuration.ui.PublicSelfServiceUiConfigs;
import com.airlock.iam.publicselfservice.infrastructure.mapper.PublicSelfServiceUiMetadataProvider;
import com.airlock.iam.publicselfservice.infrastructure.service.delay.UnverifiedUserResponseDelayCondition;
import com.airlock.iam.publicselfservice.infrastructure.service.ui.PublicSelfServiceUiRepository;
import com.airlock.iam.publicselfservice.infrastructure.service.ui.PublicSelfServiceUiService;
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 jakarta.inject.Provider;
import java.util.Optional;

public class PublicSelfServiceInfrastructureModule extends PrivateModule {
   @Provides
   @RequestScoped
   @Exposed
   PublicSelfServiceUiConfig providePublicSelfServiceUiConfig(Optional<PublicSelfServiceUiConfig> uiConfig, FlowConfig flowConfig) {
      return ViolationException.checkPresent(uiConfig, String.format("No UI config found for public self-service flow with ID '%s'", flowConfig.getFlowId()));
   }

   @Provides
   @Exposed
   Optional<PublicSelfServiceUiConfig> provideOptionalPublicSelfServicUiConfig(Optional<PublicSelfServiceUiConfigs> uiConfigs, Optional<FlowConfig> flowConfig) {
      return flowConfig.filter(PublicSelfServiceFlowConfig.class::isInstance).flatMap(flow -> uiConfigs.flatMap(c -> c.byFlowId(flow.getFlowId())));
   }

   @Provides
   @Exposed
   Optional<PublicSelfServiceUiService> provideOptionalUiPublicSelfServicService(
      Optional<PublicSelfServiceUiConfig> uiConfig, Provider<PublicSelfServiceUiService> uiService
   ) {
      return uiConfig.map(config -> (PublicSelfServiceUiService)uiService.get());
   }

   protected void configure() {
      this.bind(ResponseDelayCondition.class).to(UnverifiedUserResponseDelayCondition.class).in(RequestScoped.class);
      this.expose(ResponseDelayCondition.class);
      this.bind(FlowResponseTransformer.class).to(DefaultFlowResponseTransformer.class);
      this.expose(FlowResponseTransformer.class);
      this.bind(SessionTerminationService.class).to(DefaultSessionTerminationService.class).in(RequestScoped.class);
      this.expose(SessionTerminationService.class);
      this.bind(GenericFlowSelectionService.class).in(RequestScoped.class);
      this.expose(GenericFlowSelectionService.class);
      this.bind(DefaultStepUiConfigRepository.class).to(PublicSelfServiceUiRepository.class).in(RequestScoped.class);
      this.expose(DefaultStepUiConfigRepository.class);
      this.bind(FlowUiConfig.class).to(PublicSelfServiceUiConfig.class).in(RequestScoped.class);
      this.expose(FlowUiConfig.class);
      this.bind(MetadataProvider.class).to(PublicSelfServiceUiMetadataProvider.class);
      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);
   }
}
