package com.atlassian.bitbucket.util;

import com.google.common.collect.ImmutableList;
import org.junit.Test;

import java.util.List;
import java.util.NoSuchElementException;
import java.util.function.Function;
import java.util.function.Predicate;

import static com.atlassian.bitbucket.util.Chainable.chain;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.partition;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;

@SuppressWarnings("ConstantConditions")
public class ChainableTest {

    private static final Function<String, Integer> STRING_TO_INT = Integer::parseInt;
    private static final List<String> STRINGS = ImmutableList.of("1", "2", "3");

    @Test
    public void testFlatMap() {
        assertThat(chain(STRINGS).flatMap(input -> newArrayList(input, input)).transform(STRING_TO_INT), contains(1, 1, 2, 2, 3, 3));
    }

    @Test(expected = NoSuchElementException.class)
    public void testGetFirstEmptyChainable() {
        chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(3)).getFirst();
    }

    @Test
    public void testGetFirstEmptyChainableDefaultValue() {
        int result = chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(3)).getFirst(4);
        assertEquals(4, result);
    }

    @Test
    public void testGetFirstNonEmptyChainable() {
        int result = chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(1)).getFirst();
        assertEquals(2, result);
    }

    @Test
    public void testGetFirstNonEmptyChainableDefaultValue() {
        int result = chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(1)).getFirst(4);
        assertEquals(2, result);
    }

    @Test
    public void testIsEmpty() {
        assertThat(chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(1)).isEmpty(), equalTo(false));
        assertThat(chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(3)).isEmpty(), equalTo(true));
    }

    @Test
    public void testIterable() {
        assertThat(chain(STRINGS).transform(STRING_TO_INT), contains(1, 2, 3));
        assertThat(chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(1)), contains(2, 3));
    }

    @Test
    public void testLimit() {
        assertThat(chain(STRINGS).limit(2), contains("1", "2"));
    }

    @Test
    public void testPartition() {
        List<List<String>> partitions = partition(STRINGS, 2);
        assertThat(partitions.get(0), contains("1", "2"));
        assertThat(partitions.get(1), contains("3"));
    }

    @Test
    public void testSkip() {
        assertThat(chain(STRINGS).skip(2), contains("3"));
    }

    @Test
    public void testToList() {
        assertThat(chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(1)).toList(), contains(2, 3));
    }

    @Test
    public void testToSet() {
        assertThat(chain(STRINGS).transform(STRING_TO_INT).filter(greaterThan(1)).toSet(), contains(2, 3));
    }

    @Test
    public void testImmutability() {
        Chainable<Integer> chain = chain(STRINGS).transform(STRING_TO_INT);
        chain.filter(greaterThan(1));
        assertThat(chain.toList(), contains(1, 2, 3));
    }

    private static Predicate<Integer> greaterThan(final int i) {
        return input -> input > i;
    }
}
