package com.atlassian.crowd.dao.group;

import com.atlassian.crowd.dao.AbstractCrowdDaoTest;
import com.atlassian.crowd.dao.CrowdModelFactory;
import com.atlassian.crowd.exception.GroupNotFoundException;
import com.atlassian.crowd.model.group.Group;
import com.atlassian.crowd.model.group.GroupType;
import com.atlassian.crowd.model.group.InternalGroup;
import com.atlassian.crowd.model.group.InternalGroupAttribute;
import com.atlassian.crowd.search.builder.Restriction;
import com.atlassian.crowd.search.query.entity.GroupQuery;
import com.atlassian.crowd.search.query.entity.restriction.constants.GroupTermKeys;
import com.atlassian.bitbucket.annotation.Dao;
import com.atlassian.bitbucket.annotation.DataSet;
import com.atlassian.stash.internal.DataSets;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.junit.Test;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;

@DataSet({DataSets.CWD_DIRECTORIES, DataSets.CWD_GROUPS})
public class GroupDAOHibernateTest extends AbstractCrowdDaoTest {

    public static final String NAME_ACTIVE_GROUP = "active_cwd_group";
    public static final String NAME_INACTIVE_GROUP = "inactive_cwd_group";

    private static final String NAME_NEW_GROUP = "new group";

    @Dao
    private GroupDAOHibernate dao;

    @Test
    public void testAttributes() throws Exception {
        InternalGroup group = dao.add(CrowdModelFactory.newGroup(NAME_NEW_GROUP, GroupType.GROUP, "description", true, DEFAULT_DIR_ID));
        dao.storeAttributes(group, ImmutableMap.of("key", (Set<String>) ImmutableSet.of("value1", "value2")));

        flush();
        evict(group, group.getId());
        group = dao.findByName(DEFAULT_DIR_ID, NAME_NEW_GROUP);

        Set<InternalGroupAttribute> groupAttributes = dao.findGroupAttributes(group.getId());
        assertEquals(2, groupAttributes.size());
        assertHasNameValue(groupAttributes, "key", "value1");
        assertHasNameValue(groupAttributes, "key", "value2");

        dao.removeAttribute(group, "key");
        flush();
        evict(group, group.getId());
        group = dao.findByName(DEFAULT_DIR_ID, NAME_NEW_GROUP);

        groupAttributes = dao.findGroupAttributes(group.getId());
        assertEquals(0, groupAttributes.size());
    }

    @Test
    public void testCreate() throws Exception {
        Group added = CrowdModelFactory.newGroup(NAME_NEW_GROUP, GroupType.GROUP, "description", true, DEFAULT_DIR_ID);
        InternalGroup created = dao.add(added);

        assertNotNull(created.getId());
        assertTrue(created.isActive());

        flush();
        evict(created, created.getId());

        created = dao.findByName(DEFAULT_DIR_ID, NAME_NEW_GROUP);
        assertEquals(added.isActive(), created.isActive());
        assertEquals(added.getDescription(), created.getDescription());
        assertEquals(added.getType(), created.getType());
    }

    @Test(expected = GroupNotFoundException.class)
    public void testRemove() throws Exception {
        InternalGroup group = null;
        try {
            group = dao.findByName(DEFAULT_DIR_ID, NAME_ACTIVE_GROUP);
        } catch (GroupNotFoundException e) {
            fail("Group " + NAME_ACTIVE_GROUP + " should exist at the start of the test");
        }

        assertNotNull(group);
        dao.remove(group);

        flush();
        evict(group, group.getId());

        dao.findByName(DEFAULT_DIR_ID, NAME_ACTIVE_GROUP);
    }

    @Test(expected = GroupNotFoundException.class)
    public void testRemoveAll() throws Exception {
        dao.removeAll(DEFAULT_DIR_ID);

        flush();

        dao.findByName(DEFAULT_DIR_ID, NAME_ACTIVE_GROUP);
    }

    @Test
    public void testSearch() throws Exception {
        List<Group> activeGroups = dao.search(DEFAULT_DIR_ID, new GroupQuery<>(
                Group.class, GroupType.GROUP, Restriction.on(GroupTermKeys.ACTIVE).exactlyMatching(true), 0, Integer.MAX_VALUE));

        assertEquals(1, activeGroups.size());
        assertEquals(NAME_ACTIVE_GROUP, activeGroups.get(0).getName());

        List<Group> inactiveGroups = dao.search(DEFAULT_DIR_ID, new GroupQuery<>(
                Group.class, GroupType.GROUP, Restriction.on(GroupTermKeys.ACTIVE).exactlyMatching(false), 0, Integer.MAX_VALUE));

        assertEquals(1, inactiveGroups.size());
        assertEquals(NAME_INACTIVE_GROUP, inactiveGroups.get(0).getName());
    }

    @Test
    public void testUpdate() throws Exception {
        InternalGroup group = dao.findByName(DEFAULT_DIR_ID, NAME_ACTIVE_GROUP);

        group.updateDetailsFrom(CrowdModelFactory.newGroup(NAME_ACTIVE_GROUP, GroupType.GROUP, "new description", false, DEFAULT_DIR_ID));
        dao.update(group);

        flush();
        evict(group, group.getId());

        group = dao.findByName(DEFAULT_DIR_ID, NAME_ACTIVE_GROUP);
        assertNotNull(group);
        assertFalse(group.isActive());
        assertEquals("new description", group.getDescription());
    }

    private void assertHasNameValue(Set<InternalGroupAttribute> groupAttributes, final String name, final String value) {
        assertTrue(Iterables.any(groupAttributes, new Predicate<InternalGroupAttribute>() {

            @Override
            public boolean apply(@Nullable InternalGroupAttribute attribute) {
                return attribute.getName().equals(name) && attribute.getValue().equals(value);
            }
        }));
    }
}
