package com.airlock.iam.flow.shared.infrastructure.mapper;

import com.airlock.iam.factor.domain.model.fido.FidoPublicKeyCredentialCreationOptions;
import com.airlock.iam.factor.domain.model.fido.registration.FidoAttestationType;
import com.airlock.iam.factor.domain.model.fido.registration.FidoAuthenticatorType;
import com.airlock.iam.factor.domain.model.fido.registration.FidoUserVerificationPreference;
import com.airlock.iam.factor.domain.model.fido.registration.ResidentKeyRequirement;
import com.airlock.iam.flow.shared.infrastructure.transfer.response.fido.FidoPublicKeyCreationOptionsData;
import com.airlock.iam.flow.shared.infrastructure.transfer.response.fido.PublicKeyCredentialDescriptorData;
import java.util.Optional;

public final class PublicKeyCredentialCreationOptionsMapper {
   public static FidoPublicKeyCreationOptionsData mapPublicKeyCreationOptions(FidoPublicKeyCredentialCreationOptions source) {
      return FidoPublicKeyCreationOptionsData.builder()
         .rp(
            FidoPublicKeyCreationOptionsData.FidoPublicKeyCredentialRpEntityData.builder()
               .id(source.getFidoUser().getRelyingPartyId().getId())
               .name(source.getRegistrationSettings().getRelyingPartyName().getName())
               .build()
         )
         .user(
            FidoPublicKeyCreationOptionsData.FidoPublicKeyCredentialUserEntityData.builder()
               .id(source.getFidoUser().getUserHandle().valueAsString())
               .name(source.getRegistrationSettings().getCredentialName().getName())
               .displayName(source.getCredentialDisplayName().getName())
               .build()
         )
         .challenge(source.getChallenge().base64Url())
         .pubKeyCredParams(
            source.getRegistrationSettings()
               .getPreferredAlgorithms()
               .stream()
               .map(alg -> FidoPublicKeyCreationOptionsData.FidoPublicKeyCredentialParametersData.publicKeyCredentialParametersData(alg.getId()))
               .toList()
         )
         .timeout(source.getRegistrationSettings().getRegistrationTimeout().toMillis())
         .excludeCredentials(
            source.getExcludedCredentialIds().stream().map(id -> PublicKeyCredentialDescriptorData.publicKeyCredentialDescriptorData(id.base64Url())).toList()
         )
         .authenticatorSelection(
            FidoPublicKeyCreationOptionsData.FidoAuthenticatorSelectionCriteriaData.builder()
               .authenticatorAttachment(map(source.getRegistrationSettings().getAuthenticatorType()).orElse(null))
               .requireResidentKey(source.getRegistrationSettings().effectiveRequireResidentKey())
               .residentKey(map(source.getRegistrationSettings().getResidentKey()))
               .userVerification(map(source.getRegistrationSettings().getUserVerification()))
               .build()
         )
         .attestation(map(source.getRegistrationSettings().getAttestationType()))
         .build();
   }

   private static Optional<FidoPublicKeyCreationOptionsData.FidoAuthenticatorAttachmentData> map(FidoAuthenticatorType authenticatorType) {
      return switch (authenticatorType) {
         case ALL -> Optional.empty();
         case BOUND_ONLY -> Optional.of(FidoPublicKeyCreationOptionsData.FidoAuthenticatorAttachmentData.PLATFORM);
         case ROAMING_ONLY -> Optional.of(FidoPublicKeyCreationOptionsData.FidoAuthenticatorAttachmentData.CROSS_PLATFORM);
      };
   }

   private static FidoPublicKeyCreationOptionsData.FidoUserVerificationRequirementData map(FidoUserVerificationPreference userVerification) {
      return switch (userVerification) {
         case REQUIRED -> FidoPublicKeyCreationOptionsData.FidoUserVerificationRequirementData.REQUIRED;
         case PREFERRED -> FidoPublicKeyCreationOptionsData.FidoUserVerificationRequirementData.PREFERRED;
         case DISCOURAGED -> FidoPublicKeyCreationOptionsData.FidoUserVerificationRequirementData.DISCOURAGED;
      };
   }

   private static FidoPublicKeyCreationOptionsData.FidoAttestationConveyancePreferenceData map(FidoAttestationType attestationType) {
      return switch (attestationType) {
         case NONE -> FidoPublicKeyCreationOptionsData.FidoAttestationConveyancePreferenceData.NONE;
         case DIRECT -> FidoPublicKeyCreationOptionsData.FidoAttestationConveyancePreferenceData.DIRECT;
         case INDIRECT -> FidoPublicKeyCreationOptionsData.FidoAttestationConveyancePreferenceData.INDIRECT;
      };
   }

   private static FidoPublicKeyCreationOptionsData.FidoResidentKeyRequirementData map(ResidentKeyRequirement residentKey) {
      return switch (residentKey) {
         case REQUIRED -> FidoPublicKeyCreationOptionsData.FidoResidentKeyRequirementData.REQUIRED;
         case PREFERRED -> FidoPublicKeyCreationOptionsData.FidoResidentKeyRequirementData.PREFERRED;
         case DISCOURAGED -> FidoPublicKeyCreationOptionsData.FidoResidentKeyRequirementData.DISCOURAGED;
      };
   }

   private PublicKeyCredentialCreationOptionsMapper() {
      throw new AssertionError("no instance allowed");
   }
}
