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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.api.infrastructure.plugin.ModuleForConfig;
import com.airlock.iam.base.api.infrastructure.plugin.PluginConfigToLogic;
import com.airlock.iam.base.misc.time.TimeProvider;
import com.airlock.iam.common.api.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.common.api.domain.service.email.EmailService;
import com.airlock.iam.common.domain.model.masking.StringMasking;
import com.airlock.iam.common.domain.model.otp.OneTimePasswordFactory;
import com.airlock.iam.common.domain.model.otp.OneTimePasswordFactoryImpl;
import com.airlock.iam.common.domain.service.user.StringValueUniquenessService;
import com.airlock.iam.common.domain.service.user.StringValueUniquenessServiceImpl;
import com.airlock.iam.core.misc.impl.authen.AuthenticationMethodFactory;
import com.airlock.iam.flow.api.domain.model.session.FlowAttributeRepository;
import com.airlock.iam.flow.api.domain.model.session.FlowSession;
import com.airlock.iam.flow.infrastructure.step.StepSessionModuleHelper;
import com.airlock.iam.flow.shared.api.domain.model.validation.UniquenessValidationService;
import com.airlock.iam.flow.shared.application.configuration.step.email.EmailChangeVerificationStepConfig;
import com.airlock.iam.flow.shared.application.service.validation.EmailChangeUniquenessValidationService;
import com.airlock.iam.flow.shared.domain.model.step.email.EmailChangeDescription;
import com.airlock.iam.flow.shared.domain.model.step.email.EmailVerificationStepSession;
import com.airlock.iam.flow.shared.domain.model.step.email.EmailVerificationStepSettings;
import com.google.inject.Exposed;
import com.google.inject.Injector;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import java.util.Optional;

@ModuleForConfig(EmailChangeVerificationStepConfig.class)
public class EmailVerificationAuthStepModule extends PrivateModule {
   @Exposed
   @Provides
   EmailService provideEmailVerificationSender(EmailChangeVerificationStepConfig config) {
      return config.getEmailService();
   }

   @Exposed
   @Provides
   OneTimePasswordFactory provideOneTimePasswordFactory(EmailChangeVerificationStepConfig config, TimeProvider timeProvider) {
      return new OneTimePasswordFactoryImpl(config::getOtpGenerator, config::getOtpValidty, timeProvider);
   }

   @Exposed
   @Provides
   EmailVerificationStepSettings provideEmailVerificationStepSettings(EmailChangeVerificationStepConfig config, PluginConfigToLogic toLogic) {
      return new EmailVerificationStepSettings(
         toLogic.getLogic(config.getEmailProvider()),
         config.getMaxFailedAttempts(),
         config.isOtpCaseSensitive(),
         config.getEmailItem().isEnableStealthMode(),
         config.isSendAsHtml()
      );
   }

   @Provides
   EmailChangeDescription provideChangeDescription(FlowAttributeRepository attributes) {
      Optional<EmailChangeDescription> attribute = attributes.tryGetAttribute(EmailChangeDescription.class);
      return attribute.orElseThrow(() -> new FailureException("No email change description available"));
   }

   @Exposed
   @Provides
   UniquenessValidationService provideValidationService(EmailChangeVerificationStepConfig config, Injector injector) {
      return !config.getEmailItem().isStealthUniquenessValidationNecessary()
         ? (user, validationMode) -> ValidationResult.valid()
         : (UniquenessValidationService)injector.getInstance(EmailChangeUniquenessValidationService.class);
   }

   @Exposed
   @Provides
   EmailChangeUniquenessValidationService.EmailChangeUniquenessValidationServiceSettings provideValidationServiceSettings(
      EmailChangeVerificationStepConfig config
   ) {
      return new EmailChangeUniquenessValidationService.EmailChangeUniquenessValidationServiceSettings(config.getEmailItem().getKey());
   }

   @Exposed
   @Provides
   EmailVerificationStepSession provideEmailVerificationStepSession(FlowSession flowSession) {
      return StepSessionModuleHelper.retrieveStepSession(flowSession, EmailVerificationStepSession.class);
   }

   @Exposed
   @Provides
   Optional<AuthenticationMethod> provideAuthenticationMethod(EmailChangeVerificationStepConfig config) {
      return Optional.of(AuthenticationMethodFactory.authMethodFor(config.getAuthenticationMethodId()));
   }

   @Exposed
   @Provides
   Optional<StringMasking> provideEmailMasking(EmailChangeVerificationStepConfig config, PluginConfigToLogic toLogic) {
      return config.getMaskingSettings().map(toLogic::getLogic);
   }

   protected void configure() {
      this.bind(StringValueUniquenessService.class).to(StringValueUniquenessServiceImpl.class);
      this.expose(StringValueUniquenessService.class);
   }
}
