package com.atlassian.bitbucket.internal.defaultreviewers.dao;

import com.atlassian.activeobjects.test.TestActiveObjects;
import com.atlassian.bitbucket.ao.AbstractAoDaoTest;
import com.atlassian.bitbucket.internal.defaultreviewers.matcher.AnyRefMatcher;
import com.atlassian.bitbucket.repository.ref.restriction.RefMatcher;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageUtils;
import com.atlassian.stash.internal.repository.ref.restriction.provider.PatternMatcher;
import com.google.common.collect.ImmutableSet;
import net.java.ao.test.jdbc.Data;
import org.junit.Before;
import org.junit.Test;

import java.util.Set;

import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;

@Data(PullRequestConditionTestDatabaseUpdater.class)
public class AoPullRequestConditionDaoTest extends AbstractAoDaoTest {

    private AoPullRequestConditionDao dao;

    public AoPullRequestConditionDaoTest() {
        super(AoReviewer.class, AoPullRequestCondition.class);
    }

    @Before
    public void setup() {
        this.dao = new AoPullRequestConditionDao(new TestActiveObjects(entityManager));
    }

    @Test
    public void testCreateAndGet() {
        String patternString = "refs/heads/test/**";
        RefMatcher sourceMatcher = new PatternMatcher(patternString, false);
        RefMatcher targetMatcher = AnyRefMatcher.getInstance();
        Set<Integer> userIds = ImmutableSet.of(1, 2);

        AoPullRequestCondition condition = dao.create(1, sourceMatcher, targetMatcher, userIds, 1);

        assertThat("Should not be a null ID", condition.getId(), notNullValue());
        assertThat("Repository ID should match", condition.getRepositoryId(), equalTo(1));
        assertThat("Source matcher type should be pattern", condition.getSourceMatcherType(),
                equalTo(sourceMatcher.getType().getId()));
        assertThat("Source matcher value should be pattern string", condition.getSourceMatcherId(),
                equalTo(patternString));
        assertThat("Target matcher type should be any", condition.getTargetMatcherType(),
                equalTo(targetMatcher.getType().getId()));
        assertThat("Target matcher value should be any", condition.getTargetMatcherId(),
                equalTo(targetMatcher.getId()));
        assertThat("Required approvals should be 1", condition.getRequiredApprovals(), equalTo(1));
        assertThat("There are two reviewers", condition.getReviewers().length, equalTo(2));

        AoPullRequestCondition fetched = dao.get(condition.getId());
        assertThat("Should not be a null ID", fetched.getId(), notNullValue());
        assertThat("Repository ID should match", fetched.getRepositoryId(), equalTo(1));
        assertThat("Source matcher type should be pattern", fetched.getSourceMatcherType(),
                equalTo(sourceMatcher.getType().getId()));
        assertThat("Source matcher value should be pattern string", fetched.getSourceMatcherId(),
                equalTo(patternString));
        assertThat("Target matcher type should be any", fetched.getTargetMatcherType(),
                equalTo(targetMatcher.getType().getId()));
        assertThat("Target matcher value should be any", fetched.getTargetMatcherId(),
                equalTo(targetMatcher.getId()));
        assertThat("Required approvals should be 1", fetched.getRequiredApprovals(), equalTo(1));
        assertThat("There are two reviewers", fetched.getReviewers().length, equalTo(2));
    }

    @Test
    public void testUpdateAndGet() {
        String patternString = "refs/heads/test/**";
        RefMatcher sourceMatcher = new PatternMatcher(patternString, false);
        RefMatcher targetMatcher = AnyRefMatcher.getInstance();
        Set<Integer> userIds = ImmutableSet.of(1, 2);

        AoPullRequestCondition condition = dao.create(1, sourceMatcher, targetMatcher, userIds, 1);

        Set<Integer> updatedUserIds = ImmutableSet.of(1, 2, 3);

        AoPullRequestCondition updated = dao.update(condition, 1, sourceMatcher, targetMatcher, updatedUserIds, 2);
        assertThat("Updated source matcher type is same", updated.getSourceMatcherType(),
                equalTo(sourceMatcher.getType().getId()));
        assertThat("Updated source matcher value is same", updated.getSourceMatcherId(),
                equalTo(sourceMatcher.getId()));
        assertThat("Required approvals should now be 2", updated.getRequiredApprovals(),
                equalTo(2));
        assertThat("There are three reviewers", updated.getReviewers().length, equalTo(3));

        AoPullRequestCondition fetched = dao.get(condition.getId());
        assertThat("Fetched source matcher type is same", fetched.getSourceMatcherType(),
                equalTo(sourceMatcher.getType().getId()));
        assertThat("Fetched source matcher value is same", fetched.getSourceMatcherId(),
                equalTo(sourceMatcher.getId()));
        assertThat("Fetched required approvals should now be 2", fetched.getRequiredApprovals(),
                equalTo(2));
        assertThat("There are three reviewers", fetched.getReviewers().length, equalTo(3));
    }

    @Test
    public void testDelete() {
        AoPullRequestCondition condition = dao.get(1);
        assertThat("Condition is not null to start with", condition, notNullValue());

        dao.delete(condition);
        AoPullRequestCondition deleted = dao.get(1);
        assertThat("Condition is now null", deleted, nullValue());
    }

    @Test
    public void testFind() {
        Page<AoPullRequestCondition> conditions = dao.find(PullRequestConditionTestDatabaseUpdater.REPO_1,
                AnyRefMatcher.getInstance(), AnyRefMatcher.getInstance(), PageUtils.newRequest(0, 100));

        assertThat("The result should not be null", conditions, notNullValue());
        assertThat("There should be one condition found", conditions.getSize(), equalTo(1));
        assertThat("Should be last page", conditions.getIsLastPage(), is(true));

        conditions.stream().findFirst().ifPresent(condition -> {
            assertThat("There should be two reviewers", condition.getReviewers().length, equalTo(2));
            assertThat("There should be one required approval", condition.getRequiredApprovals(), equalTo(1));
        });
    }

    @Test
    public void testFindWithANullMatcher() {
        Page<AoPullRequestCondition> conditions = dao.find(PullRequestConditionTestDatabaseUpdater.REPO_1,
                PullRequestConditionTestDatabaseUpdater.testPatternMatcher, null, PageUtils.newRequest(0, 100));

        assertThat("The result should not be null", conditions, notNullValue());
        assertThat("There should be one condition found", conditions.getSize(), equalTo(1));
        assertThat("Should be last page", conditions.getIsLastPage(), is(true));

        conditions.stream().findFirst().ifPresent(condition -> {
            assertThat("There should be one reviewer", condition.getReviewers().length, equalTo(1));
            assertThat("There should be zero required approvals", condition.getRequiredApprovals(), equalTo(0));
        });
    }

    @Test
    public void testFindWithNoMatchers() {
        Page<AoPullRequestCondition> conditions = dao.find(PullRequestConditionTestDatabaseUpdater.REPO_1,
                PageUtils.newRequest(0, 100));

        assertThat("The result should not be null", conditions, notNullValue());
        assertThat("There should be two conditions found", conditions.getSize(), equalTo(2));
        assertThat("Should be last page", conditions.getIsLastPage(), is(true));
    }

    @Test
    public void testFindWithNoResults() {
        RefMatcher notMatching = new PatternMatcher("not-matching", false);

        Page<AoPullRequestCondition> conditions = dao.find(PullRequestConditionTestDatabaseUpdater.REPO_1,
                notMatching, notMatching, PageUtils.newRequest(0, 100));

        assertThat("The result should not be null", conditions, notNullValue());
        assertThat("There should be zero conditions found", conditions.getSize(), equalTo(0));
    }

    @Test
    public void testDeleteForRepository() {
        Page<AoPullRequestCondition> conditions = dao.find(PullRequestConditionTestDatabaseUpdater.REPO_2,
                PageUtils.newRequest(0, 100));

        assertThat("The result should not be null", conditions, notNullValue());
        assertThat("There should be two conditions found", conditions.getSize(), equalTo(2));

        dao.deleteForRepository(PullRequestConditionTestDatabaseUpdater.REPO_2);

        Page<AoPullRequestCondition> deletedConditions = dao.find(PullRequestConditionTestDatabaseUpdater.REPO_2,
                PageUtils.newRequest(0, 100));

        assertThat("The result should not be null", deletedConditions, notNullValue());
        assertThat("There should be zero conditions found", deletedConditions.getSize(), equalTo(0));
    }
}
