package com.atlassian.bitbucket.mesh.git.builder.foreachref;

import com.atlassian.bitbucket.mesh.git.ref.Ref;
import org.junit.Test;

import java.lang.reflect.Method;
import java.util.function.Predicate;
import java.util.regex.Pattern;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class RefNameFilterTest {

    @Test
    public void testFilterBy() {
        Predicate<Ref> filter = RefNameFilter.filterBy("btu");
        //Check some branches
        assertTrue(filter.test(mockRefWithId("ref/heads/STASH-1515-bturner-ref-filtering")));
        assertTrue(filter.test(mockRefWithId("ref/heads/none/bturner-ref-filtering")));
        assertFalse(filter.test(mockRefWithId("ref/heads/feature/STASH-1515-ref-filtering")));
        assertFalse(filter.test(mockRefWithId("ref/heads/feature/STASH-1515-ref-wabturn")));

        //Check some tags
        assertTrue(filter.test(mockRefWithId("ref/tags/bturner-v1")));
        assertTrue(filter.test(mockRefWithId("ref/tags/v2-bturner-ref-filtering-feature")));
        assertFalse(filter.test(mockRefWithId("ref/tags/ref-filtering-m1")));
        assertFalse(filter.test(mockRefWithId("ref/tags/ref-wabturn-filtering-m2")));

        assertTrue(filter.test(mockRefWithId("bturner-johnson-2")));
        assertFalse(filter.test(mockRefWithId("wabturn"))); //Not found in the middle of a word
        assertFalse(filter.test(mockRefWithId("!@#%")));    //Not found in a "word" of nothing but non-word characters
        assertFalse(filter.test(null));              //Null refs should never be accepted
    }

    @Test
    public void testFilterByBlank() {
        Predicate<Ref> filter = RefNameFilter.filterBy("\t\n    ");
        assertTrue(filter.test(mockRefWithId("master")));
        assertFalse(filter.test(null));
    }

    @Test
    public void testFilterByEmpty() {
        Predicate<Ref> filter = RefNameFilter.filterBy("");
        assertTrue(filter.test(mockRefWithId("master")));
        assertFalse(filter.test(null));
    }

    @Test
    public void testFilterByNull() {
        Predicate<Ref> filter = RefNameFilter.filterBy(null);
        assertTrue(filter.test(mockRefWithId("master")));
        assertFalse(filter.test(null));
    }

    @Test
    public void testFilterPattern() throws Exception {
        Method method = RefNameFilter.class.getDeclaredMethod("createPattern", String.class);
        method.setAccessible(true);

        Pattern pattern = (Pattern) method.invoke(null, "%");
        assertTrue(pattern.matcher("%").find());
        assertTrue(pattern.matcher("a%").find());   //After "a" is a word boundary, so % matches
        assertTrue(pattern.matcher("!@#%").find()); //All characters are word boundary characters, so % matches
        assertFalse(pattern.matcher("STASH-1515-bturner-ref-filtering").find()); //Not found in a string without it
        assertTrue(pattern.matcher("ref/heads/found-%").find());
        assertTrue(pattern.matcher("ref/tags/found-%-v1").find());

        pattern = (Pattern) method.invoke(null, "@");
        assertTrue(pattern.matcher("!@#%").find());
        assertTrue(pattern.matcher("!@#$%").find());
        assertTrue(pattern.matcher("@#$%").find());
        assertTrue(pattern.matcher("@#%").find());
        assertFalse(pattern.matcher("STASH-1515-bturner-ref-filtering").find());
        assertTrue(pattern.matcher("ref/heads/located@-at").find());
        assertTrue(pattern.matcher("ref/tags/found-@-v1").find());

        pattern = (Pattern) method.invoke(null, "btu");
        assertTrue(pattern.matcher("STASH-1515-bturner-ref-filtering").find());
        assertTrue(pattern.matcher("bturner-johnson-2").find());
        assertFalse(pattern.matcher("wabturn").find()); //Not found in the middle of a word
        assertFalse(pattern.matcher("!@#%").find());    //Not found in a "word" of nothing but non-word characters
        assertTrue(pattern.matcher("ref/heads/none/another-branch-bturner-ref-filtering").find());
        assertTrue(pattern.matcher("ref/tags/v1-bturner").find());
        assertFalse(pattern.matcher("ref/tags/v1-wabturn").find());
    }

    private static Ref mockRefWithId(String id) {
        Ref ref = mock(Ref.class);
        when(ref.getId()).thenReturn(id);

        return ref;
    }
}