package com.atlassian.stash.internal.repository;

import com.atlassian.bitbucket.annotation.Dao;
import com.atlassian.bitbucket.annotation.DataSet;
import com.atlassian.stash.internal.AbstractDaoTest;
import com.atlassian.stash.internal.ApplicationConstants;
import com.atlassian.stash.internal.DataSets;
import com.atlassian.stash.internal.setting.HibernateSharedLobDao;
import com.atlassian.stash.internal.setting.InternalSharedLob;
import com.atlassian.stash.internal.setting.SharedLobDao;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

@DataSet({DataSets.PROJECTS, DataSets.REPOSITORIES, DataSets.SHARED_LOBS, DataSets.REPOSITORY_HOOKS})
@SuppressWarnings("ConstantConditions")
public class HibernateRepositoryHookDaoTest extends AbstractDaoTest {

    private static final String NEW_DATA = "\"newKey\"=\"newValue\"}";

    @Dao
    private HibernateRepositoryHookDao dao;
    @Dao(HibernateRepositoryDao.class)
    private RepositoryDao repositoryDao;
    @Dao(HibernateSharedLobDao.class)
    private SharedLobDao sharedLobDao;

    @Test
    public void testCreate() {
        InternalRepository repositoryOne = repositoryDao.loadById(1);

        InternalRepositoryHook hook = new InternalRepositoryHook.Builder()
                .enabled(true)
                .hookKey("hookKey1") //Used already on repository two, so this helps verify unique constraints
                .repository(repositoryOne)
                .settings(NEW_DATA)
                .build();
        InternalRepositoryHook created = dao.create(hook);
        assertNotNull(created.getId());

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

        assertEquals(hook.getId(), created.getId());

        InternalRepositoryHook loadedHook = dao.getById(created.getId());
        assertEquals("hookKey1", loadedHook.getHookKey());
        assertEquals(NEW_DATA, loadedHook.getSettings().getData());
    }

    @Test
    public void testCreateWithNewSettings() {
        InternalRepository repositoryOne = repositoryDao.loadById(1);

        InternalSharedLob lob = new InternalSharedLob.Builder().data(NEW_DATA).build();
        InternalRepositoryHook hook = new InternalRepositoryHook.Builder()
                .enabled(true)
                .hookKey("hookKey1") //Used already on repository two, so this helps verify unique constraints
                .repository(repositoryOne)
                .settings(lob)
                .build();
        InternalRepositoryHook created = dao.create(hook);
        assertNotNull(created.getId());
        assertNotNull(lob.getId());

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

        assertEquals(hook.getId(), created.getId());

        InternalRepositoryHook loaded = dao.getById(created.getId());
        assertEquals("hookKey1", loaded.getHookKey());
        assertEquals(NEW_DATA, loaded.getSettings().getData());
    }

    @Test
    public void testFindByKey() {
        InternalRepository one = repositoryDao.loadById(1);
        InternalRepository two = repositoryDao.loadById(2);
        assertNull(dao.findByKey(one, "hookKey1")); //No hooks for this repository, hook with this key
        assertEquals(1L, dao.findByKey(two, "hookKey1").getId());
        assertNull(dao.findByKey(two, "hookKey3")); //No hook with this key
    }

    @Test
    public void testFindEnabledByKey() {
        List<InternalRepositoryHook> hooks = dao.findEnabledByKey(repositoryDao.loadById(2), Arrays.asList("hookKey1", "hookKey2"));
        assertNotNull(hooks);
        assertEquals(1, hooks.size());
        assertEquals(1L, hooks.get(0).getId());
    }
    @Test
    public void testUpdate() {
        InternalRepositoryHook initial = dao.getById(1L);
        assertTrue("The loaded hook is not enabled; this test is invalid!", initial.isEnabled());

        InternalRepositoryHook modified = new InternalRepositoryHook.Builder(initial)
                .enabled(false)
                .build();
        dao.update(modified);

        flush();
        evict(modified, modified.getId());
        evict(initial.getSettings(), initial.getSettings().getId());

        InternalRepositoryHook updated = dao.getById(initial.getId());
        assertFalse(updated.isEnabled());
    }

    @Test
    public void testUpdateAddingSettings() {
        InternalRepositoryHook initial = dao.getById(2L);
        assertNull("The loaded hook has settings; this test is invalid!", initial.getSettings());

        InternalRepositoryHook modified = new InternalRepositoryHook.Builder(initial)
                .settings(new InternalSharedLob.Builder().data("Data!").build())
                .build();

        assertEquals("The settings should be transient (no ID)",
                ApplicationConstants.UNSAVED_ENTITY, modified.getSettings().getId());
        dao.update(modified);
        assertNotNull("The settings should have been persisted (ID assigned)", modified.getSettings().getId());

        flush();
        evict(modified, modified.getId());
        evict(modified.getSettings(), modified.getSettings().getId());

        InternalRepositoryHook updated = dao.getById(initial.getId());
        assertEquals("Data!", updated.getSettings().getData());
    }

    @Test
    public void testUpdateRemovingSettings() {
        InternalRepositoryHook initial = dao.getById(1L);

        InternalSharedLob settings = initial.getSettings();
        assertNotNull("The loaded hook has no settings; this test is invalid!", settings);

        InternalRepositoryHook modified = new InternalRepositoryHook.Builder(initial)
                .settings((String) null)
                .build();
        dao.update(modified);

        flush();
        clear();

        InternalRepositoryHook updated = dao.getById(initial.getId());
        assertNull(updated.getSettings());
        assertNull(sharedLobDao.getById(settings.getId()));
    }

    @Test
    public void testUpdateUpdatingSettings() {
        InternalRepositoryHook initial = dao.getById(1L);
        assertNotNull("The loaded hook has no settings; this test is invalid!", initial.getSettings());

        InternalRepositoryHook modified = new InternalRepositoryHook.Builder(initial)
                .enabled(false)
                .settings(new InternalSharedLob.Builder(initial.getSettings()).data("Updated data!").build())
                .build();
        dao.update(modified);

        flush();
        evict(modified, modified.getId());
        evict(modified.getSettings(), modified.getSettings().getId());

        InternalRepositoryHook updated = dao.getById(initial.getId());
        assertFalse(updated.isEnabled());
        assertEquals("Updated data!", updated.getSettings().getData());
    }
}
