package com.airlock.iam.oauth2.domain.service.tokenexchange.scope;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.misc.result.Result;
import com.airlock.iam.oauth2.api.domain.model.OAuth2Scope;
import com.airlock.iam.oauth2.api.domain.model.tokenexchange.OAuth2TokenExchangeGrantRequest;
import com.airlock.iam.oauth2.api.domain.model.tokenexchange.jwt.OAuth2TokenExchangeValidatedTokenData;
import com.airlock.iam.oauth2.application.configuration.tokenexchange.rules.jwt.OAuth2TokenExchangeJwtScopeHandlingConfig;
import com.airlock.iam.oauth2.domain.model.client.OAuth2Client;
import com.airlock.iam.oauth2.domain.model.scopepolicy.ScopePolicyInput;
import com.airlock.iam.oauth2.domain.model.tokenexchange.rules.scope.OAuth2TokenExchangeScopeProcessor;
import com.airlock.iam.oauth2.domain.service.scopepolicy.ScopePolicy;
import com.airlock.iam.oauth2.domain.service.tokenexchange.OAuth2TokenExchangeGrantTokenIssuerError;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.inject.Provider;
import java.util.List;
import java.util.Set;

@LogicForConfig(OAuth2TokenExchangeJwtScopeHandlingConfig.class)
public final class OAuth2TokenExchangeScopeHandlerImpl implements OAuth2TokenExchangeScopeHandler {
   private final Provider<OAuth2TokenExchangeScopeHandlerSettings> settings;
   private final Provider<List<OAuth2TokenExchangeScopeProcessor>> processors;
   private final Provider<ScopePolicy> scopePolicy;

   @Inject
   public OAuth2TokenExchangeScopeHandlerImpl(
      Provider<OAuth2TokenExchangeScopeHandlerSettings> settings,
      Provider<List<OAuth2TokenExchangeScopeProcessor>> processors,
      @Named("TOKEN_EXCHANGE_SCOPE_POLICY") Provider<ScopePolicy> scopePolicy
   ) {
      this.settings = settings;
      this.processors = processors;
      this.scopePolicy = scopePolicy;
   }

   @Override
   public Result<Set<OAuth2Scope>, OAuth2TokenExchangeGrantTokenIssuerError> processScope(
      OAuth2TokenExchangeGrantRequest grantRequest, OAuth2TokenExchangeValidatedTokenData subjectTokenData, OAuth2Client client
   ) {
      Set<OAuth2Scope> grantedScopes = Set.of();

      for (OAuth2TokenExchangeScopeProcessor processor : (List)this.processors.get()) {
         grantedScopes = processor.process(grantedScopes, grantRequest, subjectTokenData, client);
      }

      if (!((ScopePolicy)this.scopePolicy.get()).isValidRequestedScope(List.copyOf(grantedScopes))) {
         return Result.failure(OAuth2TokenExchangeGrantTokenIssuerError.tokenExchangeGrantIssuerError("Invalid requested scope (scope policy violated)."));
      }

      grantedScopes = Set.copyOf(((ScopePolicy)this.scopePolicy.get()).processScopes(new ScopePolicyInput(List.copyOf(grantedScopes), client.allowedScopes())));
      return !((OAuth2TokenExchangeScopeHandlerSettings)this.settings.get()).emptyScopeAllowed() && grantedScopes.isEmpty()
         ? Result.failure(OAuth2TokenExchangeGrantTokenIssuerError.tokenExchangeGrantIssuerError("Scope value is empty."))
         : Result.result(grantedScopes);
   }
}
