package com.airlock.iam.admin.infrastructure.factory.prot;

import com.airlock.iam.admin.api.domain.model.administrator.CurrentAdministrator;
import com.airlock.iam.admin.application.configuration.matrixcard.MatrixCardGeneratorConfig;
import com.airlock.iam.admin.application.configuration.matrixcard.MatrixTokenController;
import com.airlock.iam.admin.application.service.matrixcard.MatrixCardAdminService;
import com.airlock.iam.admin.application.service.matrixcard.MatrixCardRepository;
import com.airlock.iam.admin.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.admin.domain.model.token.TokenController;
import com.airlock.iam.admin.domain.model.token.matrixcard.MatrixAuthenticationMethod;
import com.airlock.iam.admin.domain.model.token.matrixcard.generator.MatrixCardCreator;
import com.airlock.iam.admin.domain.model.token.matrixcard.generator.MatrixCardGenerator;
import com.airlock.iam.admin.domain.model.token.matrixcard.generator.MatrixCardWriter;
import com.airlock.iam.admin.domain.model.token.matrixcard.generator.MatrixCardWriterImpl;
import com.airlock.iam.admin.domain.model.token.matrixcard.persistence.MatrixCardPersistenceConverter;
import com.airlock.iam.base.api.domain.model.exception.ServiceConfigurationException;
import com.airlock.iam.core.misc.persistency.TokenListPersister;
import com.airlock.iam.core.misc.tanserver.core.TokenType.Alphabet;
import com.airlock.iam.core.misc.util.password.hash.IdentityPasswordHash;
import com.google.inject.Exposed;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import com.google.inject.servlet.RequestScoped;
import jakarta.inject.Named;
import jakarta.inject.Provider;
import java.time.Duration;
import java.util.Optional;

public class MatrixCardServiceModule extends PrivateModule {
   @Provides
   MatrixTokenController tokenController(Provider<TokenController.TokenControllers> tokenControllers) {
      return (MatrixTokenController)ServiceConfigurationException.checkPresent(
         ((TokenController.TokenControllers)tokenControllers.get()).ofId("MATRIX"), "Matrix token controller must be configured."
      );
   }

   @Provides
   TokenListPersister tokenListPersister(MatrixTokenController controller) {
      return controller.getTokenListPersister();
   }

   @Provides
   Optional<Duration> validityDuration(MatrixTokenController controller) {
      return controller.getValidity();
   }

   @Provides
   MatrixCardCreator.MatrixCardCreatorSettings creatorSettings(MatrixTokenController controller) {
      Optional<MatrixCardGeneratorConfig> config = controller.getMatrixCardGeneratorConfig();
      return new MatrixCardCreator.MatrixCardCreatorSettings(
         config.map(MatrixCardGeneratorConfig::getTokenAlphabet).orElse(Alphabet.DIGITS),
         config.map(MatrixCardGeneratorConfig::getTokenLength).orElse(0),
         config.map(MatrixCardGeneratorConfig::getTokensPerList).orElse(0),
         config.map(MatrixCardGeneratorConfig::getHashFunction).orElse(new IdentityPasswordHash())
      );
   }

   @Provides
   MatrixCardWriterImpl.MatrixCardWriterSettings writerSettings(MatrixTokenController controller) {
      Optional<MatrixCardGeneratorConfig> config = controller.getMatrixCardGeneratorConfig();
      return new MatrixCardWriterImpl.MatrixCardWriterSettings(
         config.map(MatrixCardGeneratorConfig::getTokenListRenderer).orElse(null),
         config.map(MatrixCardGeneratorConfig::isDeleteOldTokenLists).orElse(false),
         config.flatMap(MatrixCardGeneratorConfig::getWorkingDirectory),
         config.flatMap(MatrixCardGeneratorConfig::getOutputDirectory),
         config.map(MatrixCardGeneratorConfig::getFileNamePrefix).orElse(""),
         config.map(MatrixCardGeneratorConfig::getFileNameSuffix).orElse("")
      );
   }

   @Provides
   Optional<MatrixCardGenerator> generator(MatrixTokenController controller, MatrixCardCreator creator, MatrixCardWriter writer) {
      return controller.getMatrixCardGeneratorConfig().isEmpty() ? Optional.empty() : Optional.of(new MatrixCardGenerator(creator, writer));
   }

   @Provides
   @RequestScoped
   @Exposed
   @Named("MATRIX")
   AuthenticationMethod provideAuthMethod(MatrixTokenController controller, Provider<MatrixCardAdminService> service, CurrentAdministrator administrator) {
      return new MatrixAuthenticationMethod(
         controller.getConfiguredId(),
         controller.isAutoOrder(),
         controller.isAutoOrderForNewUsers(),
         controller.mayBeSelectedAsAuthMethod(),
         controller.mayBeSelectedAsNextAuthMethod(),
         service,
         administrator
      );
   }

   protected void configure() {
      this.bind(MatrixCardRepository.class);
      this.bind(MatrixCardPersistenceConverter.class);
      this.bind(MatrixCardCreator.class);
      this.bind(MatrixCardWriter.class).to(MatrixCardWriterImpl.class);
      this.bind(MatrixCardAdminService.class).in(RequestScoped.class);
      this.expose(MatrixCardAdminService.class);
   }
}
