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

import com.airlock.iam.oauth2.api.domain.model.OAuth2Scope;
import com.airlock.iam.techclientreg.domain.model.registration.OAuth2ClientRegistrationRequestImpl;
import com.airlock.iam.techclientreg.domain.model.step.OAuth2ClientRegistrationResponseData;
import com.airlock.iam.techclientreg.infrastructure.transfer.request.OAuth2ClientRegistrationRequestContainer;
import com.airlock.iam.techclientreg.infrastructure.transfer.response.JwkData;
import com.airlock.iam.techclientreg.infrastructure.transfer.response.JwkSetData;
import com.airlock.iam.techclientreg.infrastructure.transfer.response.OAuth2ClientRegistrationData;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.jwk.JWKSet;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import ma.glasnost.orika.CustomMapper;
import ma.glasnost.orika.DefaultFieldMapper;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.MappingException;

public class OAuth2TechClientRegistrationMapper {
   public OAuth2TechClientRegistrationMapper(MapperFactory factory) {
      this.configure(factory);
   }

   private void configure(MapperFactory factory) {
      registerClientRegistration(factory);
      registerClientRegistrationData(factory);
      registerJwkSet(factory);
   }

   private static void registerClientRegistration(MapperFactory factory) {
      factory.classMap(OAuth2ClientRegistrationRequestContainer.class, OAuth2ClientRegistrationRequestImpl.class)
         .field("metadata", "metadata.metadata")
         .field("registrationData.softwareStatement", "softwareStatement")
         .register();
   }

   private static void registerClientRegistrationData(MapperFactory factory) {
      factory.classMap(OAuth2ClientRegistrationResponseData.class, OAuth2ClientRegistrationData.class)
         .exclude("clientIdIssuedAt")
         .customize(new OAuth2TechClientRegistrationMapper.OAuth2ClientRegistrationResponseMapper())
         .byDefault(new DefaultFieldMapper[0])
         .register();
   }

   private static String mapScopes(List<OAuth2Scope> scopes) {
      return scopes != null && !scopes.isEmpty() ? scopes.stream().map(OAuth2Scope::name).collect(Collectors.joining(" ")) : null;
   }

   private static void registerJwkSet(MapperFactory factory) {
      factory.classMap(JWKSet.class, JwkSetData.class).customize(new OAuth2TechClientRegistrationMapper.JWKSetJwkSetDataCustomMapper()).register();
   }

   private static final class JWKSetJwkSetDataCustomMapper extends CustomMapper<JWKSet, JwkSetData> {
      public void mapAtoB(JWKSet jwkSet, JwkSetData jwkSetData, MappingContext context) {
         try {
            String jsonString = jwkSet.toString();
            Map<String, Object> map = (Map<String, Object>)new ObjectMapper().readValue(jsonString, new TypeReference<Map<String, Object>>() {});
            List<Map<String, Object>> keys = (List<Map<String, Object>>)map.get("keys");
            List<JwkData> collect = keys.stream().map(JwkData::new).collect(Collectors.toList());
            jwkSetData.setKeys(collect);
         } catch (IOException e) {
            throw new MappingException("could not map JWKSet because of JSON parse error", e);
         }
      }
   }

   private static class OAuth2ClientRegistrationResponseMapper extends CustomMapper<OAuth2ClientRegistrationResponseData, OAuth2ClientRegistrationData> {
      public void mapAtoB(OAuth2ClientRegistrationResponseData domain, OAuth2ClientRegistrationData dto, MappingContext context) {
         domain.getTechClientId().ifPresent(techClientId -> dto.setTechClientId(techClientId.asString()));
         domain.getClientSecret().ifPresent(secret -> {
            dto.setClientSecret(secret.getSecret());
            dto.setClientSecretExpiresAt(secret.getExpiresAt().map(Instant::getEpochSecond).orElse(0L));
         });
         domain.getClientIdIssuedAt().ifPresent(instant -> dto.setClientIdIssuedAt(instant.getEpochSecond()));
         domain.getClientNameTranslations().forEach(dto::addClientNameTranslation);
         domain.getClientUriTranslations().forEach(dto::addclientUriTranslation);
         domain.getLogoUriTranslations().forEach(dto::addLogoUriTranslation);
         domain.getTosUriTranslations().forEach(dto::addtosUriTranslation);
         domain.getPolicyUriTranslations().forEach(dto::addPolicyUriTranslation);
         dto.setScope(OAuth2TechClientRegistrationMapper.mapScopes(domain.getScopes()));
      }
   }
}
