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.oauth2.api.domain.model.OAuth2Scope;
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.ScopeProcessorConfig;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

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

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

   @Override
   public Optional<OAuth2ClientRegistrationError> updateClient(OAuth2ClientRegistrationRequest request, OAuth2TechnicalClientMetadataBuilder clientBuilder) {
      List<OAuth2Scope> verifiedScopes = this.extractAndVerifyScopes(request);
      if (!verifiedScopes.isEmpty()) {
         LOG.debug(
            "Using the following verified scopes from the request: " + verifiedScopes.stream().map(LogUtil::logSafeString).collect(Collectors.joining(", "))
         );
         clientBuilder.scopes(verifiedScopes);
      }

      return Optional.empty();
   }

   private List<OAuth2Scope> extractAndVerifyScopes(OAuth2ClientRegistrationRequest request) {
      return request.getMetadata().scopes().stream().filter(this::isValidScope).map(OAuth2Scope::of).collect(Collectors.toList());
   }

   private boolean isValidScope(String scope) {
      return this.validator
         .isValidMetadataAttribute(
            "scope", scope, 200, v -> ((ScopeProcessorConfig)this.config.get()).getAllowedScopes().stream().anyMatch(p -> p.matcher(v).matches())
         );
   }

   @SuppressFBWarnings(justification = "generated code")
   public static ScopeProcessor.ScopeProcessorBuilder builder() {
      return new ScopeProcessor.ScopeProcessorBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class ScopeProcessorBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Provider<ScopeProcessorConfig> config;
      @SuppressFBWarnings(justification = "generated code")
      private ClientMetadataAttributeValidator validator;

      @SuppressFBWarnings(justification = "generated code")
      ScopeProcessorBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public ScopeProcessor.ScopeProcessorBuilder config(Provider<ScopeProcessorConfig> config) {
         this.config = config;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ScopeProcessor.ScopeProcessorBuilder validator(ClientMetadataAttributeValidator validator) {
         this.validator = validator;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ScopeProcessor build() {
         return new ScopeProcessor(this.config, this.validator);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "ScopeProcessor.ScopeProcessorBuilder(config=" + this.config + ", validator=" + this.validator + ")";
      }
   }
}
