package com.atlassian.crowd.dao.application;

import com.atlassian.crowd.dao.AbstractCrowdDaoTest;
import com.atlassian.crowd.embedded.api.OperationType;
import com.atlassian.crowd.embedded.api.PasswordCredential;
import com.atlassian.crowd.exception.ApplicationNotFoundException;
import com.atlassian.crowd.model.application.Application;
import com.atlassian.crowd.model.application.ApplicationImpl;
import com.atlassian.crowd.model.application.ApplicationType;
import com.atlassian.crowd.search.query.entity.ApplicationQuery;
import com.atlassian.bitbucket.annotation.Dao;
import com.atlassian.bitbucket.annotation.DataSet;
import com.atlassian.stash.internal.DataSets;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;

import java.util.List;

@DataSet({DataSets.CWD_APPLICATIONS, DataSets.CWD_DIRECTORIES})
public class ApplicationDAOHibernateTest extends AbstractCrowdDaoTest {

    private static final String NAME_ACTIVE_APP = "active_cwd_application";
    private static final String NAME_INACTIVE_APP = "inactive_cwd_application";
    private static final String NAME_NEW_APP = "new app";

    @Dao
    private ApplicationDAOHibernate dao;

    @Test
    public void testCreate() throws Exception {
        ApplicationImpl added = ApplicationImpl.newInstance(NAME_NEW_APP, ApplicationType.STASH);
        ApplicationImpl created = dao.add(added, new PasswordCredential("encrypted password", true));

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

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

        created = dao.findByName(NAME_NEW_APP);
        assertEquals(added.isActive(), created.isActive());
        assertEquals(added.getType(), created.getType());
    }

    @Test
    public void testDirectoryMapping() throws Exception {
        //Execute various directory mapping operations and check they don't fail
        dao.addDirectoryMapping(dao.findByName(NAME_ACTIVE_APP).getId(), DEFAULT_DIR_ID, true, OperationType.CREATE_GROUP);

        flush();

        dao.updateDirectoryMapping(dao.findByName(NAME_ACTIVE_APP).getId(), DEFAULT_DIR_ID, false,
                ImmutableSet.of(OperationType.CREATE_GROUP, OperationType.CREATE_ROLE));

        flush();

        dao.removeDirectoryMappings(dao.findByName(NAME_ACTIVE_APP).getId());

        //Final flush needed to ensure the preceding operation required
        flush();
    }

    @Test(expected = ApplicationNotFoundException.class)
    public void testRemove() throws Exception {
        ApplicationImpl app = null;
        try {
            app = dao.findByName(NAME_ACTIVE_APP);
        } catch (ApplicationNotFoundException e) {
            fail("App " + NAME_ACTIVE_APP + " should exist at the start of the test");
        }

        assertNotNull(app);
        dao.remove(app);

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

        dao.findByName(NAME_ACTIVE_APP);
    }

    @Test
    public void testSearch() throws Exception {
        List<Application> activeApps = dao.search(new ApplicationQuery(
                IS_ACTIVE.exactlyMatching(true), 0, Integer.MAX_VALUE));

        assertEquals(1, activeApps.size());
        assertEquals(NAME_ACTIVE_APP, activeApps.get(0).getName());

        List<Application> inactiveApps = dao.search(new ApplicationQuery(
                IS_ACTIVE.exactlyMatching(false), 0, Integer.MAX_VALUE));

        assertEquals(1, inactiveApps.size());
        assertEquals(NAME_INACTIVE_APP, inactiveApps.get(0).getName());
    }

    @Test
    public void testUpdate() throws Exception {
        ApplicationImpl app = dao.findByName(NAME_ACTIVE_APP);

        app.setActive(false);
        app.setDescription("new description");
        dao.update(app);

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

        app = dao.findByName(NAME_ACTIVE_APP);
        assertNotNull(app);
        assertFalse(app.isActive());
        assertEquals("new description", app.getDescription());
    }
}
