package com.airlock.iam.techclientreg.domain.model.registration;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.techclient.api.domain.model.oauth2.OAuth2TechnicalClientMetadataBuilder;
import com.airlock.iam.techclientreg.api.domain.model.oauth2.OAuth2ClientRegistrationError;
import com.airlock.iam.techclientreg.api.domain.model.oauth2.OAuth2ClientRegistrationRequest;
import com.airlock.iam.techclientreg.api.domain.service.oauth2.OAuth2ClientMetadataProcessor;
import com.airlock.iam.techclientreg.application.configuration.registration.UrisProcessorConfig;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.net.URI;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Map.Entry;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@LogicForConfig(UrisProcessorConfig.class)
public class UrisProcessor implements OAuth2ClientMetadataProcessor {
   private static final ELogger LOG = ELoggerFactory.getLogger(UrisProcessor.class);
   private final Provider<UrisProcessorConfig> config;
   private final ClientMetadataAttributeValidator validator;

   @Inject
   public UrisProcessor(Provider<UrisProcessorConfig> config, ClientMetadataAttributeValidator validator) {
      this.config = config;
      this.validator = validator;
   }

   @Override
   public Optional<OAuth2ClientRegistrationError> updateClient(OAuth2ClientRegistrationRequest request, OAuth2TechnicalClientMetadataBuilder clientBuilder) {
      Optional<URI> clientUri = this.extractValidClientUri(request);
      if (clientUri.isEmpty() && ((UrisProcessorConfig)this.config.get()).isClientUriMandatory()) {
         return Optional.of(OAuth2ClientRegistrationError.invalidClientMetadata("client_uri missing or invalid"));
      }

      if (clientUri.isPresent()) {
         LOG.debug("Using client_uri value received from the client: " + LogUtil.logSafeString(clientUri.get()));
         clientBuilder.clientUri(clientUri)
            .clientUriTranslations(this.collectValidTranslations(request.getMetadata().clientUriTranslations(), this::isValidClientUri));
      }

      Optional<URI> logoUri = this.extractValidLogoUri(request);
      if (logoUri.isEmpty() && ((UrisProcessorConfig)this.config.get()).isLogoUriMandatory()) {
         return Optional.of(OAuth2ClientRegistrationError.invalidClientMetadata("logo_uri missing or invalid"));
      }

      if (logoUri.isPresent()) {
         LOG.debug("Using logo_uri value received from the client: " + LogUtil.logSafeString(logoUri.get()));
         clientBuilder.logoUri(logoUri).logoUriTranslations(this.collectValidTranslations(request.getMetadata().logoUriTranslations(), this::isValidLogoUri));
      }

      Optional<URI> policyUri = this.extractValidPolicyUri(request);
      if (policyUri.isEmpty() && ((UrisProcessorConfig)this.config.get()).isPolicyUriMandatory()) {
         return Optional.of(OAuth2ClientRegistrationError.invalidClientMetadata("policy_uri missing or invalid"));
      }

      if (policyUri.isPresent()) {
         LOG.debug("Using policy_uri value received from the client: " + LogUtil.logSafeString(policyUri.get()));
         clientBuilder.policyUri(policyUri)
            .policyUriTranslations(this.collectValidTranslations(request.getMetadata().policyUriTranslations(), this::isValidPolicyUri));
      }

      Optional<URI> tosUri = this.extractValidTosUri(request);
      if (tosUri.isEmpty() && ((UrisProcessorConfig)this.config.get()).isTosUriMandatory()) {
         return Optional.of(OAuth2ClientRegistrationError.invalidClientMetadata("tos_uri missing or invalid"));
      }

      if (tosUri.isPresent()) {
         LOG.debug("Using tos_uri value received from the client: " + LogUtil.logSafeString(tosUri.get()));
         clientBuilder.tosUri(tosUri).tosUriTranslations(this.collectValidTranslations(request.getMetadata().tosUriTranslations(), this::isValidTosUri));
      }

      return Optional.empty();
   }

   private Optional<URI> extractValidClientUri(OAuth2ClientRegistrationRequest request) {
      return request.getMetadata().clientUri().filter(this::isValidClientUri);
   }

   private boolean isValidClientUri(URI uri) {
      return this.validator.isValidMetadataAttribute("client_uri", uri.toString(), 200, ((UrisProcessorConfig)this.config.get()).getAllowedClientUris());
   }

   private Optional<URI> extractValidLogoUri(OAuth2ClientRegistrationRequest request) {
      return request.getMetadata().logoUri().filter(this::isValidLogoUri);
   }

   private boolean isValidLogoUri(URI uri) {
      return this.validator.isValidMetadataAttribute("logo_uri", uri.toString(), 200, ((UrisProcessorConfig)this.config.get()).getAllowedLogoUris());
   }

   private Optional<URI> extractValidPolicyUri(OAuth2ClientRegistrationRequest request) {
      return request.getMetadata().policyUri().filter(this::isValidPolicyUri);
   }

   private boolean isValidPolicyUri(URI uri) {
      return this.validator.isValidMetadataAttribute("policy_uri", uri.toString(), 200, ((UrisProcessorConfig)this.config.get()).getAllowedPolicyUris());
   }

   private Optional<URI> extractValidTosUri(OAuth2ClientRegistrationRequest request) {
      return request.getMetadata().tosUri().filter(this::isValidTosUri);
   }

   private boolean isValidTosUri(URI uri) {
      return this.validator.isValidMetadataAttribute("tos_uri", uri.toString(), 200, ((UrisProcessorConfig)this.config.get()).getAllowedTosUris());
   }

   private Map<Locale, URI> collectValidTranslations(Map<Locale, URI> translations, Predicate<URI> isValid) {
      return translations.entrySet().stream().filter(e -> isValid.test(e.getValue())).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
   }
}
