package com.airlock.iam.oauth2.application.service;

import com.airlock.iam.base.misc.result.Result;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.api.domain.model.OAuth2ClientId;
import com.airlock.iam.oauth2.api.domain.model.OAuth2Scope;
import com.airlock.iam.oauth2.domain.model.resource.OAuth2Resource;
import com.airlock.iam.oauth2.domain.model.resource.OAuth2ResourceResult;
import com.airlock.iam.oauth2.domain.model.validation.OAuth2ValidationResult;
import com.airlock.iam.oauth2.domain.model.validation.OAuth2ValidationStatus;
import jakarta.inject.Inject;
import java.util.Optional;
import java.util.Set;

public final class OAuth2ResourceService {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2ResourceService.class);
   private final OAuth2Resource resource;

   @Inject
   public OAuth2ResourceService(OAuth2Resource resource) {
      this.resource = resource;
   }

   public Result<OAuth2ResourceResult, OAuth2ValidationResult> getMatchingResource(Set<OAuth2Scope> givenScopes, PersistentUser user, OAuth2ClientId clientId) {
      if (!this.resource.hasMatchingScopes(givenScopes)) {
         LOG.info(
            String.format(
               "Not enough scopes to access requested resource. Given scopes: %s, required scopes: %s",
               OAuth2Scope.convertToScopeNames(givenScopes),
               OAuth2Scope.convertToScopeNames(this.resource.getRequiredScopes())
            )
         );
         return Result.failure(new OAuth2ValidationResult(OAuth2ValidationStatus.SCOPES_REQUIRED));
      } else {
         OAuth2ResourceResult result = this.resource
            .getResourceProviders()
            .stream()
            .map(provider -> provider.getResourceResult(user, givenScopes, clientId))
            .flatMap(Optional::stream)
            .reduce(OAuth2ResourceResult.resourceResult(), OAuth2ResourceResult::merge);
         if (result.isEmpty()) {
            LOG.info("None of the resource providers provided a result");
            return Result.failure(new OAuth2ValidationResult(OAuth2ValidationStatus.UNEXPECTED));
         } else {
            return Result.result(result);
         }
      }
   }
}
