package com.airlock.iam.base.misc.collection;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

public final class IamMaps {
   public static <K, V> Map<K, V> mergeMaps(Map<K, V> a, Map<K, V> b, Consumer<? super K> consumeCollisionKey) {
      Map<K, V> result = new HashMap<>(a);
      b.forEach((key, value) -> result.merge((K)key, (V)value, (oldValue, newValue) -> {
         if (result.containsKey(key) && !Objects.equals(oldValue, newValue)) {
            consumeCollisionKey.accept((K)key);
         }

         return (V)newValue;
      }));
      return Collections.unmodifiableMap(result);
   }

   public static <K, V> Map<K, V> mergeMaps(Map<K, V> a, Map<K, V> b) {
      return mergeMaps(a, b, nop -> {});
   }

   public static <K, V> Map<K, V> mergeMaps(Map<K, V> a, Map<K, V> b, Map<K, V> c) {
      return mergeMaps(mergeMaps(a, b), c);
   }

   public static Map<String, Object> mergeMapsRecursively(Map<String, Object> a, Map<String, Object> b) {
      Map<String, Object> result = new HashMap<>(a);
      b.forEach((key, value) -> {
         if (!result.containsKey(key)) {
            result.put(key, value);
         } else {
            if (!(value instanceof Map) || !(result.get(key) instanceof Map)) {
               throw new FailureException("Cannot merge maps with conflicting keys: " + key);
            }

            result.put(key, mergeMapsRecursively((Map<String, Object>)result.get(key), (Map<String, Object>)value));
         }
      });
      return result;
   }

   private IamMaps() {
      throw new AssertionError();
   }
}
