package com.airlock.iam.oauth2.application.configuration.client;

import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.domain.service.client.resource.OAuth2ResourceSelector;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.license.PluginLicenseRestriction;
import com.airlock.iam.plugin.api.annotation.property.RegexListProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Map.Entry;
import java.util.regex.Pattern;

@Plugin(
   description = "Plugin selecting a specific resource from a parsed response.<br>To find a resource in the response tree, the configured regex patterns are applied to the identifiers in the following way:<br>The first pattern of the matcher list is applied to the keys of the root level. The element corresponding to the first matching key is retrieved and the next pattern is applied to the keys of the nested element.<br>The last pattern must apply to a single value or a list of values (then the first one is used).",
   displayName = "OAuth 2.0 Resource Selector"
)
@PluginLicenseRestriction(moduleAffiliation = "OAuthClient")
public final class OAuth2ResourceSelectorImpl implements OAuth2ResourceSelector {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2ResourceSelectorImpl.class);
   @RegexListProperty(
      description = "List of patterns that will be applied in sequence on the keyset of nested maps. If the pattern is ambiguous (multiple matches) the first one is selected.",
      exampleElements = {"user.*", "name"},
      optional = false,
      minElements = 1
   )
   private List<Pattern> matcherList;

   @Override
   public <T> Optional<T> retrieveResource(Map<String, Object> resourceMap, Class<T> type) {
      return this.retrieveResource(resourceMap, type, 0);
   }

   private <T> Optional<T> retrieveResource(Map<String, Object> resourceMap, Class<T> type, int patternIndex) {
      Pattern currentPattern = this.matcherList.get(patternIndex);

      for (Entry<String, Object> entry : resourceMap.entrySet()) {
         if (currentPattern.matcher(entry.getKey()).matches()) {
            if (patternIndex < this.matcherList.size() - 1) {
               Map<String, Object> nestedMap = (Map<String, Object>)entry.getValue();
               return this.retrieveResource(nestedMap, type, patternIndex + 1);
            }

            Object rawValue = entry.getValue();
            return ResourceSelectorHelper.determineResultingValue(rawValue, entry.getKey(), type, resourceMap);
         }
      }

      LOG.info("The OAuth 2.0 resource was not found. No key matches any of the configured patterns: " + this.matcherList);
      return Optional.empty();
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public OAuth2ResourceSelectorImpl() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public OAuth2ResourceSelectorImpl(List<Pattern> matcherList) {
      this.matcherList = matcherList;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class OAuth2ResourceSelectorImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private List<Pattern> matcherList;

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

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourceSelectorImpl.OAuth2ResourceSelectorImplBuilder matcherList(List<Pattern> matcherList) {
         this.matcherList = matcherList;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public OAuth2ResourceSelectorImpl build() {
         return new OAuth2ResourceSelectorImpl(this.matcherList);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "OAuth2ResourceSelectorImpl.OAuth2ResourceSelectorImplBuilder(matcherList=" + this.matcherList + ")";
      }
   }
}
