package com.atlassian.stash.internal.user;

import com.atlassian.bitbucket.annotation.Dao;
import com.atlassian.bitbucket.annotation.DataSet;
import com.atlassian.stash.internal.AbstractDaoTest;
import com.atlassian.stash.internal.DataSets;
import com.atlassian.stash.internal.repository.HibernateRepositoryDao;
import com.atlassian.stash.internal.repository.InternalRepository;
import com.atlassian.stash.internal.repository.RepositoryDao;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import org.junit.Test;

import java.util.Date;
import java.util.List;
import java.util.function.Predicate;

@DataSet(DataSets.REPOSITORY_ACCESSES)
public class HibernateRepositoryAccessDaoTest extends AbstractDaoTest {

    private static final Predicate<InternalRepositoryAccess> ALL_ACCESSES = a -> true;

    @Dao
    private HibernateRepositoryAccessDao dao;
    @Dao(HibernateRepositoryDao.class)
    private RepositoryDao repositoryDao;
    @Dao(HibernateStashUserDao.class)
    private StashUserDao userDao;

    @Test
    public void testCreate() {
        Date now = new Date();
        InternalRepositoryAccess access = new InternalRepositoryAccess.Builder()
                .lastAccessed(now)
                .repository(repositoryDao.loadById(1))
                .user(userDao.loadById(1))
                .build();
        dao.create(access);

        flush();
        clear();

        InternalRepositoryAccess created = dao.getById(new InternalRepositoryAccess.PK(1, 1));
        assertEquals(1, created.getRepository().getId());
        assertEquals(1, created.getUser().getId());
        assertEquals(access.getLastAccessed(), created.getLastAccessed());
    }

    @Test
    public void testDelete() {
        InternalRepositoryAccess access = dao.getById(new InternalRepositoryAccess.PK(3, 2));
        assertNotNull(access);
        dao.delete(access);

        flush();
        clear();

        assertNull(dao.getById(new InternalRepositoryAccess.PK(3, 2)));
    }

    @Test
    public void testDeleteAllAccessesForRepository() {
        assertEquals(2, dao.deleteAllAccessesForRepository(1));
        assertEquals(2, dao.deleteAllAccessesForRepository(2));
        assertEquals(2, dao.deleteAllAccessesForRepository(3));
        assertEquals(0, dao.deleteAllAccessesForRepository(4)); //No such repository
    }

    @Test
    public void testDeleteAllAccessesForUser() {
        assertEquals(0, dao.deleteAllAccessesForUser(1)); //User exists, has no accesses
        assertEquals(3, dao.deleteAllAccessesForUser(3));
        assertEquals(0, dao.deleteAllAccessesForUser(5)); //No such user
    }

    @Test
    public void testFindRecentRepositories() {
        Page<InternalRepository> page = dao.findRecentRepositories(3, PageUtils.newRequest(0, 10), ALWAYS_TRUE);
        assertEquals(3, page.getSize());
        List<InternalRepository> repositories = ImmutableList.copyOf(page.getValues());
        assertEquals(3, repositories.get(0).getId());
        assertEquals(1, repositories.get(1).getId());
        assertEquals(2, repositories.get(2).getId());
    }

    @Test
    public void testFindRecentRepositoriesWithNoAccesses() {
        Page<InternalRepository> page = dao.findRecentRepositories(1, PageUtils.newRequest(0, 10), ALWAYS_TRUE);
        assertNotNull(page);
        assertTrue(page.getIsLastPage());
        assertEquals(0, page.getSize());
    }

    @Test
    public void testTrimRecentRepositories() {
        dao.trimRecentRepositories(3, 2, ALL_ACCESSES);

        flush();
        clear();

        Page<InternalRepositoryAccess> page = dao.findAll(PageUtils.newRequest(0, 10));
        assertNotNull(page);
        assertTrue(page.getIsLastPage());
        assertEquals(5, page.getSize());

        List<InternalRepositoryAccess> accesses = ImmutableList.copyOf(Iterables.filter(page.getValues(),
                access -> access.getUser().getId() == 3));
        assertEquals(2, accesses.size());
        assertEquals(1365140688L, accesses.get(0).getLastAccessed().getTime());
        assertEquals(1365140684L, accesses.get(1).getLastAccessed().getTime());
    }

    @Test
    public void testTrimRecentRepositoriesWithSameAccessDates() {
        dao.trimRecentRepositories(2, 2, ALL_ACCESSES);

        flush();
        clear();

        Page<InternalRepositoryAccess> page = dao.findAll(PageUtils.newRequest(0, 10));
        assertNotNull(page);
        assertTrue(page.getIsLastPage());
        assertEquals(6, page.getSize());

        List<InternalRepositoryAccess> accesses = ImmutableList.copyOf(Iterables.filter(page.getValues(),
                access -> access.getUser().getId() == 2));
        assertEquals(3, accesses.size());
        assertEquals(1365143134L, accesses.get(0).getLastAccessed().getTime());
        assertEquals(1365143100L, accesses.get(1).getLastAccessed().getTime());
        assertEquals(1365143100L, accesses.get(2).getLastAccessed().getTime());
    }

    @Test
    public void testUpdate() {
        InternalRepositoryAccess access = dao.getById(new InternalRepositoryAccess.PK(3, 1));
        assertNotNull(access);

        Date now = new Date();
        dao.update(new InternalRepositoryAccess.Builder(access).lastAccessed(now).build());

        flush();
        clear();

        InternalRepositoryAccess updated = dao.getById(new InternalRepositoryAccess.PK(3, 1));
        assertEquals(now, updated.getLastAccessed());
    }

    @Test
    public void testUpdateCreatesIfNonexistent() {
        assertNull(dao.getById(new InternalRepositoryAccess.PK(1, 1)));

        Date now = new Date();
        InternalRepositoryAccess access = new InternalRepositoryAccess.Builder()
                .lastAccessed(now)
                .repository(repositoryDao.loadById(1))
                .user(userDao.loadById(1))
                .build();
        dao.update(access);

        flush();
        clear();

        InternalRepositoryAccess created = dao.getById(new InternalRepositoryAccess.PK(1, 1));
        assertEquals(1, created.getRepository().getId());
        assertEquals(1, created.getUser().getId());
        assertEquals(access.getLastAccessed(), created.getLastAccessed());
    }
}
