package com.airlock.iam.saml2.infrastructure.factory.sp;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.infrastructure.plugin.ModuleForConfig;
import com.airlock.iam.base.api.infrastructure.plugin.PluginConfigToLogic;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.authmethod.AuthenticationMethod;
import com.airlock.iam.saml2.application.configuration.sp.Saml2FlowSpConfig;
import com.airlock.iam.saml2.application.configuration.sp.Saml2SpAssertionStringAttributeImportConfig;
import com.airlock.iam.saml2.application.configuration.sp.Saml2UserIdentifyingStepConfig;
import com.airlock.iam.saml2.domain.model.assertion.attribute.Saml2AssertionAttributeImport;
import com.airlock.iam.saml2.domain.model.assertion.attribute.Saml2AssertionAttributesImporter;
import com.airlock.iam.saml2.domain.model.assertion.attribute.Saml2AssertionAttributesImporterImpl;
import com.airlock.iam.saml2.domain.model.assertion.attribute.Saml2AttributeNameToTagMapping;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpEntityId;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpInitiatedSsoId;
import com.airlock.iam.saml2.domain.model.sp.Saml2SpInitiatedSsoUriFactory;
import com.airlock.iam.saml2.domain.service.Saml2FlowSpConfigService;
import com.airlock.iam.saml2.domain.service.Saml2TagsFromAttributesService;
import com.airlock.iam.saml2.infrastructure.service.sp.Saml2TagsFromAttributesServiceImpl;
import com.google.inject.Exposed;
import com.google.inject.PrivateModule;
import com.google.inject.Provides;
import java.util.Collection;
import java.util.List;

@ModuleForConfig(Saml2UserIdentifyingStepConfig.class)
public final class Saml2UserIdentifyingStepModule extends PrivateModule {
   @Provides
   Saml2FlowSpConfig provideSpConfig(Saml2FlowSpConfigService spConfigService, Saml2SpEntityId entityId) {
      return spConfigService.findFlowSpConfig(entityId)
         .orElseThrow(() -> new FailureException("Missing SAML 2.0 flow SP config for SP entity ID " + LogUtil.logSafeQuote(entityId)));
   }

   @Provides
   @Exposed
   Saml2SpEntityId provideSaml2SpEntityId(Saml2UserIdentifyingStepConfig config) {
      return config.getSaml2SpEntityId();
   }

   @Provides
   @Exposed
   List<Saml2AttributeNameToTagMapping> provideSaml2AttributeNameToTagMappings(Saml2FlowSpConfig spConfig, PluginConfigToLogic configToLogic) {
      return spConfig.getAttributeNameToTagMappingConfigs().stream().map(configToLogic::getLogic).map(Saml2AttributeNameToTagMapping.class::cast).toList();
   }

   @Provides
   @Exposed
   AuthenticationMethod provideAuthenticationMetthod(Saml2UserIdentifyingStepConfig config) {
      return config.getAuthenticationMethod();
   }

   @Provides
   @Exposed
   Saml2SpInitiatedSsoId provideSaml2SpInitiatedSsoId() {
      return Saml2SpInitiatedSsoId.newSpInitiatedSsoId();
   }

   @Provides
   @Exposed
   Collection<? extends Saml2AssertionAttributeImport> provideSaml2AssertionAttributeImports(Saml2FlowSpConfig config) {
      return config.getAttributesToImport().stream().map(this::mapSingleAttributeImport).toList();
   }

   private Saml2AssertionAttributeImport mapSingleAttributeImport(Saml2SpAssertionStringAttributeImportConfig config) {
      return config.isFailIfEmpty()
         ? Saml2AssertionAttributeImport.mandatoryAssertionAttribute(config.getAttributeName(), config.getKey())
         : Saml2AssertionAttributeImport.optionalAssertionAttribute(config.getAttributeName(), config.getKey());
   }

   protected void configure() {
      this.bind(Saml2SpInitiatedSsoUriFactory.class).to(Saml2SpInitiatedSsoUriFactoryImpl.class);
      this.expose(Saml2SpInitiatedSsoUriFactory.class);
      this.bind(Saml2AssertionAttributesImporter.class).to(Saml2AssertionAttributesImporterImpl.class);
      this.expose(Saml2AssertionAttributesImporter.class);
      this.bind(Saml2TagsFromAttributesService.class).to(Saml2TagsFromAttributesServiceImpl.class);
      this.expose(Saml2TagsFromAttributesService.class);
   }
}
