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

import com.airlock.iam.base.api.domain.model.event.source.EventSource;
import com.airlock.iam.common.domain.model.principal.MaxFailedAttempts;
import com.airlock.iam.common.domain.model.principal.NopMaxFailedAttempts;
import com.airlock.iam.flow.api.application.configuration.step.FlowStepConfig;
import com.airlock.iam.flow.application.configuration.FlowConfig;
import com.airlock.iam.flow.application.configuration.FlowConfigRepository;
import com.airlock.iam.flow.application.configuration.flow.FlowConfigHash;
import com.airlock.iam.flow.application.service.flow.GenericFlowConfigRepository;
import com.airlock.iam.flow.application.service.session.FlowContinuationChecker;
import com.airlock.iam.flow.application.service.session.GenericFlowSessionRepository;
import com.airlock.iam.flow.domain.model.session.FlowSessionRepository;
import com.airlock.iam.flow.domain.model.statemachine.FlowState;
import com.airlock.iam.flow.domain.service.EmptyFlowCompletionService;
import com.airlock.iam.flow.domain.service.FlowCompletionService;
import com.airlock.iam.flow.domain.service.MultiFlowManager;
import com.airlock.iam.flow.infrastructure.FlowContinuationCheckerImpl;
import com.airlock.iam.flow.infrastructure.flow.event.FlowEventSourceImpl;
import com.airlock.iam.flow.shared.domain.service.FlowSessionUserSessionConnector;
import com.airlock.iam.flow.shared.domain.service.NoFlowSessionUserSessionConnector;
import com.airlock.iam.flow.types.api.domain.model.session.KnownMultiFlowType;
import com.airlock.iam.flow.types.api.domain.model.session.MultiFlowType;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.techclientreg.application.configuration.TechClientRegFlowConfig;
import com.airlock.iam.techclientreg.application.configuration.TechClientRegistrationRestConfig;
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;

public class TechClientRegFlowModule extends PrivateModule {
   @Provides
   @Exposed
   @RequestScoped
   FlowConfigRepository provideFlowConfigRepository(MultiFlowManager multiFlowManager, TechClientRegistrationRestConfig config) {
      return GenericFlowConfigRepository.builder()
         .multiFlowManager(multiFlowManager)
         .flowConfigs(List.of(config.getDefaultFlow()))
         .defaultFlowId(Optional.of(config.getDefaultFlow().getFlowId()))
         .build();
   }

   @Provides
   Optional<FlowConfigHash> provideFlowConfigHash(TechClientRegFlowConfig flowConfig) {
      return Optional.of(flowConfig.getFlowConfigHash());
   }

   @Provides
   @Exposed
   @RequestScoped
   TechClientRegFlowConfig provideDefaultFlowConfig(TechClientRegistrationRestConfig config) {
      return config.getDefaultFlow();
   }

   @Exposed
   @Provides
   MaxFailedAttempts provideMaxFailedAttempts() {
      return new NopMaxFailedAttempts();
   }

   @Exposed
   @Provides
   EventSource provideFlowEventSource(ConfigurationContext context, FlowConfig config, FlowState flowState) {
      return FlowEventSourceImpl.flowEventSource(
         context, config.getFlowId(), KnownMultiFlowType.TECH_CLIENT_REG, flowState.getStepConfig().flatMap(FlowStepConfig::getStepId)
      );
   }

   protected void configure() {
      this.bind(FlowSessionUserSessionConnector.class).to(NoFlowSessionUserSessionConnector.class).in(RequestScoped.class);
      this.expose(FlowSessionUserSessionConnector.class);
      this.bind(FlowContinuationChecker.class).to(FlowContinuationCheckerImpl.class);
      this.bind(FlowSessionRepository.class).to(GenericFlowSessionRepository.class).in(RequestScoped.class);
      this.expose(FlowSessionRepository.class);
      this.bind(GenericFlowSessionRepository.class).in(RequestScoped.class);
      this.expose(GenericFlowSessionRepository.class);
      this.bind(FlowCompletionService.class).to(EmptyFlowCompletionService.class);
      this.expose(FlowCompletionService.class);
      this.bind(MultiFlowType.class).toInstance(KnownMultiFlowType.TECH_CLIENT_REG);
      this.expose(MultiFlowType.class);
   }
}
