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

import com.airlock.iam.core.misc.authen.result.AuthenticationFailed;
import com.airlock.iam.core.misc.impl.authen.cronto.CrontoPushDeviceChoice;
import com.airlock.iam.flow.api.domain.model.flow.FlowResult;
import com.airlock.iam.flow.shared.domain.model.airlock2fa.Airlock2FAOfflineQrCodeChallenge;
import com.airlock.iam.flow.shared.domain.model.cronto.CrontoAuthChallenge;
import com.airlock.iam.flow.shared.domain.model.cronto.CrontoPushDevice;
import com.airlock.iam.keymanagementservice.domain.model.KmsPublicE2eEncryptionInformation;
import com.airlock.iam.keymanagementservice.infrastructure.transfer.response.KmsPublicE2eEncryptionInformationData;
import com.airlock.iam.publicselfservice.api.infrastructure.transfer.response.PublicSelfServiceFlowResultData;
import com.airlock.iam.publicselfservice.domain.model.step.SecretQuestionsIdentityVerificationStep;
import com.airlock.iam.publicselfservice.infrastructure.mapper.converter.FidoApprovalChallengeConverter;
import com.airlock.iam.publicselfservice.infrastructure.transfer.PublicSelfServiceAirlock2FAOfflineChallengeData;
import com.airlock.iam.publicselfservice.infrastructure.transfer.PublicSelfServiceCrontoChallengeData;
import com.airlock.iam.publicselfservice.infrastructure.transfer.PublicSelfServiceCrontoPushDeviceData;
import com.airlock.iam.publicselfservice.infrastructure.transfer.PublicSelfServiceMtanTokenData;
import com.airlock.iam.publicselfservice.infrastructure.transfer.PublicSelfServiceSecretQuestionsChallengeData;
import ma.glasnost.orika.CustomConverter;
import ma.glasnost.orika.DefaultFieldMapper;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.metadata.Type;
import ma.glasnost.orika.metadata.TypeFactory;

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

   private void configureAll(MapperFactory factory) {
      configureFlowResultData(factory);
      configureSecretQuestions(factory);
      configureMtanTokenDataForMtanChoice(factory);
      configureCrontoPushDeviceChoiceData(factory);
      configureAirlock2FAChallengeData(factory);
      factory.getConverterFactory().registerConverter(new PublicSelfServiceRestBeanMappings.CrontoChallengeConverter());
      configureKmsPublicE2eEncryptionInformation(factory);
      factory.getConverterFactory().registerConverter(new FidoApprovalChallengeConverter());
   }

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

   private static void configureSecretQuestions(MapperFactory factory) {
      factory.classMap(SecretQuestionsIdentityVerificationStep.SecretQuestions.class, PublicSelfServiceSecretQuestionsChallengeData.class)
         .field("questions:{questions}", "questions")
         .register();
   }

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

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

   private static void configureAirlock2FAChallengeData(MapperFactory factory) {
      factory.classMap(Airlock2FAOfflineQrCodeChallenge.class, PublicSelfServiceAirlock2FAOfflineChallengeData.class)
         .field("challengeImage.encodedImage", "challengeImage")
         .register();
   }

   private static PublicSelfServiceCrontoChallengeData.CrontoPushDeviceData mapPushDevice(CrontoPushDevice crontoPushDevice) {
      return PublicSelfServiceCrontoChallengeData.CrontoPushDeviceData.builder()
         .id(crontoPushDevice.id())
         .label(crontoPushDevice.label().orElse(null))
         .platform(crontoPushDevice.platform().orElse(null))
         .build();
   }

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

   private static final class CrontoChallengeConverter extends CustomConverter<CrontoAuthChallenge, PublicSelfServiceCrontoChallengeData> {
      public PublicSelfServiceCrontoChallengeData convert(
         CrontoAuthChallenge source, Type<? extends PublicSelfServiceCrontoChallengeData> destinationType, MappingContext mappingContext
      ) {
         return PublicSelfServiceCrontoChallengeData.builder()
            .challengeImage(source.getBase64Cryptogram())
            .secureChannelChallenge(source.getSecureChannelChallenge().orElse("-"))
            .onlineValidation(source.isOnlineValidation())
            .pushed(source.isPushed())
            .pushDevices(source.getPushDevices().stream().map(PublicSelfServiceRestBeanMappings::mapPushDevice).toList())
            .build();
      }

      public boolean canConvert(Type<?> sourceType, Type<?> destinationType) {
         Type<Object> objectType = TypeFactory.valueOf(Object.class);
         Type<PublicSelfServiceCrontoChallengeData> publicSelfServiceCrontoChallengeDataType = TypeFactory.valueOf(PublicSelfServiceCrontoChallengeData.class);
         return this.sourceType.equals(sourceType) && (destinationType.equals(objectType) || destinationType.equals(publicSelfServiceCrontoChallengeDataType));
      }
   }
}
