/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.dubbo.common.utils;

import org.apache.dubbo.config.ProtocolConfig;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty;
import static org.apache.dubbo.common.utils.CollectionUtils.isNotEmpty;
import static org.apache.dubbo.common.utils.CollectionUtils.ofSet;
import static org.apache.dubbo.common.utils.CollectionUtils.toMap;
import static org.apache.dubbo.common.utils.CollectionUtils.toStringMap;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class CollectionUtilsTest {
    @Test
    void testSort() {
        List<Integer> list = new ArrayList<Integer>();
        list.add(100);
        list.add(10);
        list.add(20);

        List<Integer> expected = new ArrayList<Integer>();
        expected.add(10);
        expected.add(20);
        expected.add(100);

        assertEquals(expected, CollectionUtils.sort(list));
    }

    @Test
    void testSortNull() {
        assertNull(CollectionUtils.sort(null));

        assertTrue(CollectionUtils.sort(new ArrayList<Integer>()).isEmpty());
    }

    @Test
    void testSortSimpleName() {
        List<String> list = new ArrayList<String>();
        list.add("aaa.z");
        list.add("b");
        list.add(null);
        list.add("zzz.a");
        list.add("c");
        list.add(null);

        List<String> sorted = CollectionUtils.sortSimpleName(list);
        assertNull(sorted.get(0));
        assertNull(sorted.get(1));
    }

    @Test
    void testSortSimpleNameNull() {
        assertNull(CollectionUtils.sortSimpleName(null));

        assertTrue(CollectionUtils.sortSimpleName(new ArrayList<String>()).isEmpty());
    }

    @Test
    void testFlip() {
        assertEquals(CollectionUtils.flip(null), null);
        Map<String, String> input1 = new HashMap<>();
        input1.put("k1", null);
        input1.put("k2", "v2");
        Map<String, String> output1 = new HashMap<>();
        output1.put(null, "k1");
        output1.put("v2", "k2");
        assertEquals(CollectionUtils.flip(input1), output1);
        Map<String, String> input2 = new HashMap<>();
        input2.put("k1", null);
        input2.put("k2", null);
        assertThrows(IllegalArgumentException.class, () -> CollectionUtils.flip(input2));
    }

    @Test
    void testSplitAll() {
        assertNull(CollectionUtils.splitAll(null, null));
        assertNull(CollectionUtils.splitAll(null, "-"));

        assertTrue(CollectionUtils.splitAll(new HashMap<String, List<String>>(), "-").isEmpty());

        Map<String, List<String>> input = new HashMap<String, List<String>>();
        input.put("key1", Arrays.asList("1:a", "2:b", "3:c"));
        input.put("key2", Arrays.asList("1:a", "2:b"));
        input.put("key3", null);
        input.put("key4", new ArrayList<String>());

        Map<String, Map<String, String>> expected = new HashMap<String, Map<String, String>>();
        expected.put("key1", CollectionUtils.toStringMap("1", "a", "2", "b", "3", "c"));
        expected.put("key2", CollectionUtils.toStringMap("1", "a", "2", "b"));
        expected.put("key3", null);
        expected.put("key4", new HashMap<String, String>());

        assertEquals(expected, CollectionUtils.splitAll(input, ":"));
    }

    @Test
    void testJoinAll() {
        assertNull(CollectionUtils.joinAll(null, null));
        assertNull(CollectionUtils.joinAll(null, "-"));

        Map<String, List<String>> expected = new HashMap<String, List<String>>();
        expected.put("key1", Arrays.asList("1:a", "2:b", "3:c"));
        expected.put("key2", Arrays.asList("1:a", "2:b"));
        expected.put("key3", null);
        expected.put("key4", new ArrayList<String>());

        Map<String, Map<String, String>> input = new HashMap<String, Map<String, String>>();
        input.put("key1", CollectionUtils.toStringMap("1", "a", "2", "b", "3", "c"));
        input.put("key2", CollectionUtils.toStringMap("1", "a", "2", "b"));
        input.put("key3", null);
        input.put("key4", new HashMap<String, String>());

        Map<String, List<String>> output = CollectionUtils.joinAll(input, ":");
        for (Map.Entry<String, List<String>> entry : output.entrySet()) {
            if (entry.getValue() == null)
                continue;
            Collections.sort(entry.getValue());
        }

        assertEquals(expected, output);
    }

    @Test
    void testJoinList() {
        List<String> list = emptyList();
        assertEquals("", CollectionUtils.join(list, "/"));

        list = Arrays.asList("x");
        assertEquals("x", CollectionUtils.join(list, "-"));

        list = Arrays.asList("a", "b");
        assertEquals("a/b", CollectionUtils.join(list, "/"));
    }

    @Test
    void testMapEquals() {
        assertTrue(CollectionUtils.mapEquals(null, null));
        assertFalse(CollectionUtils.mapEquals(null, new HashMap<String, String>()));
        assertFalse(CollectionUtils.mapEquals(new HashMap<String, String>(), null));

        assertTrue(CollectionUtils.mapEquals(CollectionUtils.toStringMap("1", "a", "2", "b"), CollectionUtils.toStringMap("1", "a", "2", "b")));
        assertFalse(CollectionUtils.mapEquals(CollectionUtils.toStringMap("1", "a"), CollectionUtils.toStringMap("1", "a", "2", "b")));
    }

    @Test
    void testStringMap1() {
        assertThat(toStringMap("key", "value"), equalTo(Collections.singletonMap("key", "value")));
    }

    @Test
    void testStringMap2() {
        Assertions.assertThrows(IllegalArgumentException.class, () -> toStringMap("key", "value", "odd"));
    }

    @Test
    void testToMap1() {
        assertTrue(CollectionUtils.toMap().isEmpty());

        Map<String, Integer> expected = new HashMap<String, Integer>();
        expected.put("a", 1);
        expected.put("b", 2);
        expected.put("c", 3);

        assertEquals(expected, CollectionUtils.toMap("a", 1, "b", 2, "c", 3));
    }

    @Test
    void testObjectToMap() throws Exception {
        ProtocolConfig protocolConfig=new ProtocolConfig();
        protocolConfig.setSerialization("fastjson2");

        assertFalse(CollectionUtils.objToMap(protocolConfig).isEmpty());
    }

    @Test
    void testToMap2() {
        Assertions.assertThrows(IllegalArgumentException.class, () -> toMap("a", "b", "c"));
    }

    @Test
    void testIsEmpty() {
        assertThat(isEmpty(null), is(true));
        assertThat(isEmpty(new HashSet()), is(true));
        assertThat(isEmpty(emptyList()), is(true));
    }

    @Test
    void testIsNotEmpty() {
        assertThat(isNotEmpty(singleton("a")), is(true));
    }

    @Test
    void testOfSet() {
        Set<String> set = ofSet();
        assertEquals(emptySet(), set);

        set = ofSet(((String[]) null));
        assertEquals(emptySet(), set);

        set = ofSet("A", "B", "C");
        Set<String> expectedSet = new LinkedHashSet<>();
        expectedSet.add("A");
        expectedSet.add("B");
        expectedSet.add("C");
        assertEquals(expectedSet, set);
    }
}
