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

import com.airlock.iam.authentication.domain.model.processor.LatestAuthentication;
import com.airlock.iam.authentication.infrastructure.transfer.request.OAuth2AuthorizationUriData;
import com.airlock.iam.authentication.infrastructure.transfer.response.Airlock2FADeviceData;
import com.airlock.iam.authentication.infrastructure.transfer.response.Airlock2FAMobileOnlyChallengeData;
import com.airlock.iam.authentication.infrastructure.transfer.response.Airlock2FAOfflineQrCodeChallengeData;
import com.airlock.iam.authentication.infrastructure.transfer.response.AuthMtanTokenData;
import com.airlock.iam.authentication.infrastructure.transfer.response.AuthenticationAirlock2FAActivationChallengeData;
import com.airlock.iam.authentication.infrastructure.transfer.response.AuthenticationCrontoPushDeviceData;
import com.airlock.iam.authentication.infrastructure.transfer.response.AuthenticationData;
import com.airlock.iam.authentication.infrastructure.transfer.response.CrontoActivationChallengeData;
import com.airlock.iam.authentication.infrastructure.transfer.response.FormPostData;
import com.airlock.iam.authentication.infrastructure.transfer.response.LatestAuthenticationData;
import com.airlock.iam.authentication.infrastructure.transfer.response.OAuth2LocalConsentAttributeData;
import com.airlock.iam.authentication.infrastructure.transfer.response.OAuth2RemoteConsentAttributeData;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.common.domain.model.auth.ExternalizationContext;
import com.airlock.iam.common.infrastructure.transfer.response.ExternalizationContextData;
import com.airlock.iam.core.misc.authen.result.AuthenticationFailed;
import com.airlock.iam.core.misc.impl.authen.cronto.CrontoPushDeviceChoice;
import com.airlock.iam.factor.domain.model.airlock2fa.Airlock2FAAuthDevice;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.auth.api.infrastructure.transfer.response.AuthenticationFlowResultData;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.Airlock2FAMobileOnlyChallenge;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.Airlock2FAOfflineQrCodeChallenge;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.activation.Airlock2FAActivationChallenge;
import com.airlock.iam.flow.shared.domain.model.cronto.CrontoActivationChallenge;
import com.airlock.iam.flow.shared.domain.model.cronto.CrontoPushActivationChallenge;
import com.airlock.iam.keymanagementservice.domain.model.KmsPublicE2eEncryptionInformation;
import com.airlock.iam.keymanagementservice.infrastructure.transfer.response.KmsPublicE2eEncryptionInformationData;
import com.airlock.iam.oauth2.domain.model.FormPostParameters;
import com.airlock.iam.oauth2.domain.model.provider.OAuth2LocalConsentAttribute;
import com.airlock.iam.oauth2.domain.model.provider.OAuth2RemoteConsentAttribute;
import com.airlock.iam.oauth2.domain.model.sso.OAuth2AuthorizationUri;
import java.util.List;
import java.util.stream.Collectors;
import ma.glasnost.orika.CustomConverter;
import ma.glasnost.orika.CustomMapper;
import ma.glasnost.orika.DefaultFieldMapper;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.metadata.Type;

public final class AuthFlowRestBeanMappings {
   public AuthFlowRestBeanMappings(MapperFactory factory) {
      this.configureAll(factory);
   }

   private void configureAll(MapperFactory factory) {
      this.configureAirlock2FADeviceData(factory);
      this.configureAirlock2FAMobileOnlyChallengeData(factory);
      this.configureMtanTokenDataForMtanChoice(factory);
      this.configureCrontoChallengeData(factory);
      configureCrontoPushDeviceChoiceData(factory);
      configureCrontoActivationChallenge(factory);
      configureFlowResultData(factory);
      configureFormPostData(factory);
      configureExternalizationContextData(factory);
      configureAuthenticationData(factory);
      configureSecretQuestionsProvision(factory);
      configureUserDataItemInfoAttributesData(factory);
      configureAuthMtanRegistrationAttributesData(factory);
      this.configureAirlock2FAMigrationChallengeData(factory);
      this.configureAirlock2FAOfflineQrCodeChallengeData(factory);
      this.configureFido(factory);
      configureLatestAuthenticationData(factory);
      this.configureOAuth2LocalConsentAttributeData(factory);
      this.configureOAuth2RemoteConsentAttributeData(factory);
      this.configureOAuth2AuthorizationRequestUriData(factory);
      configureKmsPublicE2eEncryptionInformation(factory);
   }

   private void configureFido(MapperFactory factory) {
      factory.getConverterFactory().registerConverter(new FidoAuthenticationChallengeConverter());
      factory.getConverterFactory().registerConverter(new FidoRegistrationChallengeConverter());
   }

   private void configureAirlock2FAMigrationChallengeData(MapperFactory factory) {
      factory.classMap(Airlock2FAActivationChallenge.class, AuthenticationAirlock2FAActivationChallengeData.class)
         .field("activationQrCodeBase64", "activationQrCode")
         .byDefault(new DefaultFieldMapper[0])
         .register();
   }

   private void configureAirlock2FAOfflineQrCodeChallengeData(MapperFactory factory) {
      factory.classMap(Airlock2FAOfflineQrCodeChallenge.class, Airlock2FAOfflineQrCodeChallengeData.class)
         .field("challengeImage.encodedImage", "challengeImage")
         .byDefault(new DefaultFieldMapper[0])
         .register();
   }

   private void configureMtanTokenDataForMtanChoice(MapperFactory factory) {
      factory.classMap(AuthenticationFailed.MtanChoiceToken.class, AuthMtanTokenData.class)
         .field("maskedPhoneNumber", "number")
         .field("preSelected", "defaultNumber")
         .byDefault(new DefaultFieldMapper[0])
         .register();
   }

   private void configureCrontoChallengeData(MapperFactory factory) {
      factory.getConverterFactory().registerConverter(new CrontoChallengeConverter());
   }

   private void configureAirlock2FADeviceData(MapperFactory factory) {
      factory.classMap(Airlock2FAAuthDevice.class, Airlock2FADeviceData.class)
         .field("displayName.value", "displayName")
         .byDefault(new DefaultFieldMapper[0])
         .register();
   }

   private void configureAirlock2FAMobileOnlyChallengeData(MapperFactory factory) {
      factory.getConverterFactory().registerConverter(new AuthFlowRestBeanMappings.Airlock2FAMobileOnlyChallengeConverter());
   }

   private void configureOAuth2LocalConsentAttributeData(MapperFactory factory) {
      factory.classMap(OAuth2LocalConsentAttribute.class, OAuth2LocalConsentAttributeData.class)
         .customize(new AuthFlowRestBeanMappings.OAuth2LocalConsentAttributeMapper())
         .register();
   }

   private void configureOAuth2RemoteConsentAttributeData(MapperFactory factory) {
      factory.classMap(OAuth2RemoteConsentAttribute.class, OAuth2RemoteConsentAttributeData.class).byDefault(new DefaultFieldMapper[0]).register();
   }

   private void configureOAuth2AuthorizationRequestUriData(MapperFactory factory) {
      factory.classMap(OAuth2AuthorizationUri.class, OAuth2AuthorizationUriData.class).byDefault(new DefaultFieldMapper[0]).register();
   }

   private static void configureCrontoPushDeviceChoiceData(MapperFactory factory) {
      factory.classMap(CrontoPushDeviceChoice.class, AuthenticationCrontoPushDeviceData.class)
         .field("preSelected", "defaultDevice")
         .byDefault(new DefaultFieldMapper[0])
         .register();
   }

   private static void configureCrontoActivationChallenge(MapperFactory factory) {
      factory.classMap(CrontoActivationChallenge.class, CrontoActivationChallengeData.class).byDefault(new DefaultFieldMapper[0]).register();
      factory.classMap(CrontoPushActivationChallenge.class, CrontoActivationChallengeData.class).byDefault(new DefaultFieldMapper[0]).register();
   }

   private static void configureFlowResultData(MapperFactory factory) {
      factory.classMap(FlowResult.class, AuthenticationFlowResultData.class).field("nextStep:{nextStep}", "nextAuthStep").register();
   }

   private static void configureFormPostData(MapperFactory factory) {
      factory.classMap(FormPostParameters.class, FormPostData.class).customize(new CustomMapper<FormPostParameters, FormPostData>() {
         public void mapAtoB(FormPostParameters parameters, FormPostData formPostData, MappingContext context) {
            formPostData.putAll(parameters.params());
         }
      }).register();
   }

   private static void configureExternalizationContextData(MapperFactory factory) {
      factory.classMap(ExternalizationContext.class, ExternalizationContextData.class).fieldMap("jwt", "context").add().register();
   }

   private static void configureAuthenticationData(MapperFactory factory) {
      factory.classMap(PersistentUser.class, AuthenticationData.class)
         .field("latestSuccessfulLogin", "latestSuccessfulAuthentication")
         .field("name", "userId")
         .register();
   }

   private static void configureSecretQuestionsProvision(MapperFactory factory) {
      factory.getConverterFactory().registerConverter(new SecretQuestionsProvisionConverter());
   }

   private static void configureUserDataItemInfoAttributesData(MapperFactory factory) {
      factory.getConverterFactory().registerConverter(new UserDataItemInfoConverter());
   }

   private static void configureAuthMtanRegistrationAttributesData(MapperFactory factory) {
      factory.getConverterFactory().registerConverter(new AuthMtanRegistrationInfoConverter());
   }

   private static void configureLatestAuthenticationData(MapperFactory factory) {
      factory.classMap(LatestAuthentication.class, LatestAuthenticationData.class).byDefault(new DefaultFieldMapper[0]).register();
   }

   private static void configureKmsPublicE2eEncryptionInformation(MapperFactory factory) {
      factory.classMap(KmsPublicE2eEncryptionInformation.class, KmsPublicE2eEncryptionInformationData.class).byDefault(new DefaultFieldMapper[0]).register();
   }

   private static class Airlock2FAMobileOnlyChallengeConverter extends CustomConverter<Airlock2FAMobileOnlyChallenge, Airlock2FAMobileOnlyChallengeData> {
      public Airlock2FAMobileOnlyChallengeData convert(
         Airlock2FAMobileOnlyChallenge source, Type<? extends Airlock2FAMobileOnlyChallengeData> destinationType, MappingContext mappingContext
      ) {
         return new Airlock2FAMobileOnlyChallengeData(source.getAuthUri().uri());
      }
   }

   private static class OAuth2LocalConsentAttributeMapper extends CustomMapper<OAuth2LocalConsentAttribute, OAuth2LocalConsentAttributeData> {
      public void mapAtoB(
         OAuth2LocalConsentAttribute oAuth2LocalConsentAttribute, OAuth2LocalConsentAttributeData oAuth2LocalConsentAttributeData, MappingContext context
      ) {
         oAuth2LocalConsentAttributeData.setClientName(oAuth2LocalConsentAttribute.getClientName());
         List<OAuth2LocalConsentAttributeData.ScopeAttributeData> grantableScopes = oAuth2LocalConsentAttribute.getGrantableScopes()
            .stream()
            .map(
               scope -> new OAuth2LocalConsentAttributeData.ScopeAttributeData(
                  scope.getScope(), scope.getTranslatedScope().orElse(null), scope.getConsentStatus().isGranted(), scope.getUpdatedAt().orElse(null)
               )
            )
            .collect(Collectors.toList());
         oAuth2LocalConsentAttributeData.setGrantableScopes(grantableScopes);
      }
   }
}
