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

import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.base.misc.log.LogUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class DistributedClaims {
   public static final String DISTRIBUTED_CLAIM_SOURCES = "_claim_sources";
   public static final String DISTRIBUTED_CLAIM_NAMES = "_claim_names";
   private final List<DistributedClaim> claims;

   public static DistributedClaims emptyDistributedClaims() {
      return new DistributedClaims(List.of());
   }

   public Map<String, String> getClaimNames() {
      Map<String, String> values = new HashMap<>();

      for (DistributedClaim distributedClaim : this.claims) {
         distributedClaim.getClaimNames().forEach(claim -> {
            if (values.put(claim, distributedClaim.getReference()) != null) {
               throw new ViolationException("Duplicate claim name: " + LogUtil.quote(claim));
            }
         });
      }

      return values;
   }

   public Map<String, String> getClaimSources() {
      return this.claims.stream().collect(Collectors.toMap(DistributedClaim::getReference, DistributedClaim::getUrl));
   }

   public DistributedClaims combineWith(DistributedClaims other) {
      List<DistributedClaim> combinedClaims = new ArrayList<>(other.claims);
      Set<String> seenClaimNames = other.claims.stream().map(DistributedClaim::getClaimNames).flatMap(Collection::stream).collect(Collectors.toSet());

      for (DistributedClaim claim : this.claims) {
         List<String> unseenClaimNames = claim.getClaimNames().stream().filter(Predicate.not(seenClaimNames::contains)).toList();
         if (!unseenClaimNames.isEmpty()) {
            combinedClaims.add(new DistributedClaim(unseenClaimNames, claim.getUrl()));
         }
      }

      return new DistributedClaims(combinedClaims);
   }

   @SuppressFBWarnings(justification = "generated code")
   public DistributedClaims(List<DistributedClaim> claims) {
      this.claims = claims;
   }
}
