package com.airlock.iam.oauth2.domain.model.resource;

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.core.application.configuration.contextdata.StringContextDataItemNameConfig;
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.api.domain.model.claims.conditions.ClaimCondition;
import com.airlock.iam.oauth2.application.configuration.resource.OAuth2StringContextDataResourceProviderConfig;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;
import java.util.Set;

@LogicForConfig(OAuth2StringContextDataResourceProviderConfig.class)
public final class OAuth2StringContextDataResourceProvider implements OAuth2ResourceProvider {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2StringContextDataResourceProvider.class);
   private final Provider<OAuth2ResourceIdentifier> identifier;
   private final Provider<StringContextDataItemNameConfig> config;
   private final Provider<Optional<ClaimCondition>> condition;

   @Inject
   public OAuth2StringContextDataResourceProvider(
      Provider<OAuth2ResourceIdentifier> identifier, Provider<StringContextDataItemNameConfig> config, Provider<Optional<ClaimCondition>> condition
   ) {
      this.identifier = identifier;
      this.config = config;
      this.condition = condition;
   }

   @Override
   public Optional<OAuth2ResourceResult> getResourceResult(PersistentUser user, Set<OAuth2Scope> givenScopes, OAuth2ClientId clientId) {
      if (((Optional)this.condition.get()).isPresent() && !((ClaimCondition)((Optional)this.condition.get()).get()).isSatisfied(givenScopes)) {
         return Optional.empty();
      } else {
         Optional<Object> data = Optional.ofNullable(user.getContextData().get(((StringContextDataItemNameConfig)this.config.get()).getContextDataName()));
         if (data.isEmpty()) {
            LOG.info(
               String.format(
                  "Missing %s context data value for user %s: Failed to add value for resource %s.",
                  LogUtil.quote(((StringContextDataItemNameConfig)this.config.get()).getContextDataName()),
                  LogUtil.quote(user.getName()),
                  LogUtil.quote(((OAuth2ResourceIdentifier)this.identifier.get()).getId())
               )
            );
            return Optional.empty();
         } else if (!(data.get() instanceof String)) {
            throw new ViolationException(
               String.format(
                  "Context data %s is not of type String, but of type %s",
                  LogUtil.quote(((StringContextDataItemNameConfig)this.config.get()).getContextDataName()),
                  LogUtil.quote(data.get().getClass().getName())
               )
            );
         } else {
            return Optional.of(OAuth2ResourceResult.resourceResult().add((OAuth2ResourceIdentifier)this.identifier.get(), data.get()));
         }
      }
   }
}
