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

import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.exception.PropertyTypeMismatchException;
import com.airlock.iam.plugin.api.valuereader.PluginListPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.PluginPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.StringListPropertyValueReader;
import com.airlock.iam.plugin.api.valuereader.StringPropertyValueReader;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public final class OAuth2ClaimsUniquenessValidator {
   private OAuth2ClaimsUniquenessValidator() {
   }

   public static void validateUniqueDistributedClaimNames(
      PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, String distributedClaimsProperty
   ) throws PropertyTypeMismatchException, PropertyConstraintViolationException {
      Set<String> seenClaimNames = new HashSet<>();

      for (PluginConfiguration distributedClaim : PluginListPropertyValueReader.effectivePluginListPropertyValue(
         config, distributedClaimsProperty, env, context
      )) {
         for (String claimName : StringListPropertyValueReader.effectiveStringListConfigValue(distributedClaim, "claimNames", env, context)) {
            if (!seenClaimNames.add(claimName)) {
               throw new PropertyConstraintViolationException(
                  distributedClaimsProperty, config, env, context, "Duplicate claim name found in distributed claims: " + LogUtil.quote(claimName)
               );
            }
         }
      }
   }

   public static void validateUniqueCustomClaimNames(
      PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, String customClaimsProperty, String distributedClaimsProperty
   ) throws PropertyTypeMismatchException, PropertyConstraintViolationException {
      List<PluginConfiguration> customClaims = PluginListPropertyValueReader.effectivePluginListPropertyValue(config, customClaimsProperty, env, context);
      Set<String> seenCustomClaimNames = new HashSet<>();
      Set<String> seenCustomClaimNamesWithCondition = new HashSet<>();
      List<PluginConfiguration> distributedClaims = PluginListPropertyValueReader.effectivePluginListPropertyValue(
         config, distributedClaimsProperty, env, context
      );
      if (!distributedClaims.isEmpty()) {
         seenCustomClaimNames.addAll(List.of("_claim_sources", "_claim_names"));
      }

      for (PluginConfiguration customClaim : customClaims) {
         String claimName = StringPropertyValueReader.effectiveStringConfigValue(customClaim, "claimName", env, context);
         if (!isConditionalClaim(customClaim, env, context) || seenCustomClaimNames.contains(claimName)) {
            if (seenCustomClaimNames.contains(claimName) || seenCustomClaimNamesWithCondition.contains(claimName)) {
               throw new PropertyConstraintViolationException(
                  customClaimsProperty, config, env, context, "Claims must be unique. Found multiple claims with name " + LogUtil.quote(claimName)
               );
            }

            seenCustomClaimNames.add(claimName);
         } else {
            seenCustomClaimNamesWithCondition.add(claimName);
         }
      }
   }

   private static boolean isConditionalClaim(PluginConfiguration customClaim, ConfigurationEnvironment env, ConfigurationContext context) throws PropertyTypeMismatchException {
      return PluginPropertyValueReader.effectivePluginPropertyValue(customClaim, "claimCondition", env, context) != null;
   }
}
