package com.atlassian.bitbucket.internal.mirroring.repositories;

import com.atlassian.bitbucket.i18n.KeyedMessage;
import com.atlassian.bitbucket.i18n.SimpleI18nService;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.*;
import com.atlassian.bitbucket.scm.mirror.ScmMirrorCommandFactory;
import com.atlassian.bitbucket.server.ApplicationPropertiesService;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.io.File;
import java.util.Optional;

import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class DefaultRepositoryContentHashServiceTest {

    @Rule
    public TemporaryFolder tmp = new TemporaryFolder();
    @Mock
    private ApplicationPropertiesService propertiesService;
    @Mock
    private Command<String> command;
    @Mock
    private ScmMirrorCommandFactory commandFactory;
    @Mock
    private Repository repository;
    private DefaultRepositoryContentHashService service;
    @Mock
    private ScmService scmService;

    @Before
    public void setUp() throws Exception {
        when(repository.getScmId()).thenReturn("supported");
        when(propertiesService.getDataDir()).thenReturn(tmp.getRoot());
        when(scmService.getMirrorCommandFactory(any(Repository.class))).thenReturn(commandFactory);
        when(scmService.isSupported(repository, ScmFeature.MIRRORS)).thenReturn(true);

        service = new DefaultRepositoryContentHashService(new SimpleI18nService(), propertiesService, scmService);
    }

    @Test
    public void testGetHashGenerates() {
        Optional<String> hash = service.getHash(repository);

        assertTrue(hash.isPresent());
    }

    @Test
    public void testGetHashReturnsCurrentHash() {
        Optional<String> hash = service.getHash(repository);

        assertTrue(hash.isPresent());
        Optional<String> secondHash = service.getHash(repository);
        assertTrue(secondHash.isPresent());
        assertEquals(hash.get(), secondHash.get());
    }

    @Test
    public void testGetHashUnavailable() {
        Repository unavailable = mock(Repository.class);
        when(scmService.isSupported(unavailable, ScmFeature.MIRRORS)).thenThrow(new UnavailableScmException(
                new KeyedMessage("", "", ""), "unavailable"));

        assertFalse(service.getHash(unavailable).isPresent());
    }

    @Test
    public void testGetHashUnsupported() {
        Repository unsupported = mock(Repository.class);
        when(scmService.isSupported(unsupported, ScmFeature.MIRRORS)).thenThrow(new UnsupportedScmException(
                new KeyedMessage("", "", ""), "unsupported"));

        assertFalse(service.getHash(unsupported).isPresent());
    }

    @Test
    public void testSetHash() {
        service.setHash(repository, "set-hash");

        Optional<String> hash = service.getHash(repository);

        assertTrue(hash.isPresent());
        assertEquals("set-hash", hash.get());
    }

    @Test(expected = IllegalArgumentException.class)
    public void testSetHashUnsupported() {
        Repository unsupported = mock(Repository.class);
        when(unsupported.getScmId()).thenReturn("unsupported");

        service.setHash(unsupported, "set-hash");
    }

    @Test(expected = ContentHashStorageException.class)
    public void testSetHashFailsDueToReadOnlyDir() throws Exception {
        File roDir = tmp.newFolder();
        assumeTrue(roDir.setWritable(false)); //Will not work on Windows

        try {
            when(propertiesService.getDataDir()).thenReturn(roDir);

            service.setHash(repository, "set-hash");
        } finally {
            assertTrue(roDir.setWritable(true));
        }
    }

    @Test
    public void testGetHashGeneratesButSetHashFailsDueToReadOnlyDir() throws Exception {
        File roDir = tmp.newFolder();
        assumeTrue(roDir.setWritable(false)); //Will not work on Windows

        try {
            when(propertiesService.getDataDir()).thenReturn(roDir);

            assertFalse(service.getHash(repository).isPresent());
        } finally {
            assertTrue(roDir.setWritable(true));
        }
    }
}