package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.cache.PackCacheService;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.repository.RepositoryManager;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcClearPackCacheRequest;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcClearPackCacheResponse;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGetPackCacheStatisticsRequest;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGetPackCacheStatisticsResponse;
import com.atlassian.util.contentcache.CacheStatistics;
import com.atlassian.util.contentcache.ContentCacheStatistics;
import io.grpc.stub.StreamObserver;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class GrpcPackCacheServiceMockTest {

    private static final String REPOSITORY_ID = "repository.id";

    @Mock
    private StreamObserver<RpcClearPackCacheResponse> clearCacheObserver;
    @Mock
    private PackCacheService packCacheService;
    @Mock
    private Repository repository;
    @Mock
    private RepositoryManager repositoryManager;
    @InjectMocks
    private GrpcPackCacheService service;
    @Captor
    private ArgumentCaptor<RpcGetPackCacheStatisticsResponse> statisticsCaptor;
    @Mock
    private StreamObserver<RpcGetPackCacheStatisticsResponse> statisticsObserver;

    @Test
    public void testClear() {
        service.clear(RpcClearPackCacheRequest.getDefaultInstance(), clearCacheObserver);

        verify(packCacheService).clear();
    }

    @Test
    public void testClearByRepository() {
        when(repositoryManager.getById(eq(REPOSITORY_ID))).thenReturn(repository);

        service.clear(RpcClearPackCacheRequest.newBuilder()
                .setRepository(REPOSITORY_ID)
                .build(), clearCacheObserver);

        verify(packCacheService).clear(same(repository));
        verify(repositoryManager).getById(eq(REPOSITORY_ID));
    }

    @Test
    public void testClearByRepositoryAndCacheKey() {
        String cacheKey = "clone|99390bbed5af744f13de66c2510ba5213c8ffbe7fcc50646de0a47abbfedab98";

        when(repositoryManager.getById(eq(REPOSITORY_ID))).thenReturn(repository);

        service.clear(RpcClearPackCacheRequest.newBuilder()
                .setCacheKey(cacheKey)
                .setRepository(REPOSITORY_ID)
                .build(), clearCacheObserver);

        verify(packCacheService).clear(same(repository), eq(cacheKey));
        verify(repositoryManager).getById(eq(REPOSITORY_ID));
    }

    @Test
    public void testGetStatisticsAll() {
        CacheStatistics statistics = mock(CacheStatistics.class);
        when(statistics.getHits()).thenReturn(1L);
        when(statistics.getMisses()).thenReturn(2L);
        when(statistics.getSize()).thenReturn(3L);
        when(packCacheService.getStatistics()).thenReturn(statistics);

        service.getStatistics(RpcGetPackCacheStatisticsRequest.getDefaultInstance(), statisticsObserver);

        verify(statisticsObserver).onNext(statisticsCaptor.capture());
        verify(statisticsObserver).onCompleted();
        RpcGetPackCacheStatisticsResponse response = statisticsCaptor.getValue();
        assertEquals(1L, response.getHits());
        assertEquals(2L, response.getMisses());
        assertEquals(3L, response.getSize());
    }

    @Test
    public void testGetStatisticsForRepository() {
        ContentCacheStatistics statistics = mock(ContentCacheStatistics.class);
        when(statistics.getHits()).thenReturn(1);
        when(statistics.getMisses()).thenReturn(2);
        when(statistics.getSize()).thenReturn(3L);
        when(packCacheService.getStatistics(repository)).thenReturn(statistics);
        when(repositoryManager.getById(eq(REPOSITORY_ID))).thenReturn(repository);

        service.getStatistics(RpcGetPackCacheStatisticsRequest.newBuilder().setRepository(REPOSITORY_ID).build(),
                statisticsObserver);

        verify(statisticsObserver).onNext(statisticsCaptor.capture());
        verify(statisticsObserver).onCompleted();
        RpcGetPackCacheStatisticsResponse response = statisticsCaptor.getValue();
        assertEquals(1L, response.getHits());
        assertEquals(2L, response.getMisses());
        assertEquals(3L, response.getSize());
    }

    @Test
    public void testGetStatisticsForRepositoryWhenNoStatisticsAvailable() {
        when(repositoryManager.getById(eq(REPOSITORY_ID))).thenReturn(repository);

        service.getStatistics(RpcGetPackCacheStatisticsRequest.newBuilder().setRepository(REPOSITORY_ID).build(),
                statisticsObserver);

        verify(statisticsObserver).onNext(statisticsCaptor.capture());
        verify(statisticsObserver).onCompleted();
        RpcGetPackCacheStatisticsResponse response = statisticsCaptor.getValue();
        assertEquals(0L, response.getHits());
        assertEquals(0L, response.getMisses());
        assertEquals(0L, response.getSize());
    }
}
