package org.xbib.logging.ext.test;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Supplier;
import org.junit.jupiter.api.Assertions;

public class MapTestUtils {

    public MapTestUtils() {
    }

    /**
     * Compares the two maps have the same keys and same values in any order.
     *
     * @param m1  the first map used to compare the keys and values
     * @param m2  the second map used to compare the keys and values
     * @param <K> the key type
     * @param <V> the value type
     */
    @SuppressWarnings("WeakerAccess")
    public static <K, V> void compareMaps(final Map<K, V> m1, final Map<K, V> m2) {
        Supplier<String> failureMessage = () -> String.format("Keys did not match%n%s%n%s%n", m1.keySet(), m2.keySet());
        Assertions.assertTrue(m1.keySet().containsAll(m2.keySet()), failureMessage);
        Assertions.assertTrue(m2.keySet().containsAll(m1.keySet()), failureMessage);

        // At this point we know that all the keys match
        for (Map.Entry<K, V> entry1 : m1.entrySet()) {
            final V value2 = m2.get(entry1.getKey());
            Assertions.assertEquals(entry1.getValue(), value2,
                    () -> String.format("Value %s from the first map does not match value %s from the second map with key %s.",
                            entry1.getValue(), value2, entry1.getKey()));
        }
    }

    /**
     * A helper to easily build maps. The resulting map is immutable and the order is predictable with the
     * {@link #add(Object, Object)} order.
     */
    public static class MapBuilder<K, V> {
        private final Map<K, V> result;

        private MapBuilder(final Map<K, V> result) {
            this.result = result;
        }

        public static <K, V> MapBuilder<K, V> create() {
            return new MapBuilder<>(new LinkedHashMap<K, V>());
        }

        public MapBuilder<K, V> add(final K key, final V value) {
            result.put(key, value);
            return this;
        }

        public Map<K, V> build() {
            return Collections.unmodifiableMap(result);
        }
    }
}
