package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.builder.GitQuarantineEnvironment;
import com.atlassian.bitbucket.mesh.repository.Repository;
import io.grpc.Context;
import org.apache.commons.lang3.mutable.Mutable;
import org.apache.commons.lang3.mutable.MutableObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import static com.atlassian.bitbucket.mesh.repository.Repository.PARAM_QUARANTINE;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class GitQuarantineRegistryTest {

    private static final String REPO1_ID = "p/000c/h/9c618ea082c48f7b0841/r/3d409a5836";
    private static final String REPO2_ID = "p/0005/h/34c948b14966e2c63d48/r/ef32094668";

    private GitQuarantineRegistry registry;
    @Mock
    private Repository repository;

    @Before
    public void setUp() {
        when(repository.getId()).thenReturn(REPO1_ID);

        registry = new GitQuarantineRegistry();
    }

    @Test
    public void testEnvironmentIdGeneration() {
        GitQuarantineEnvironment env1 = new GitQuarantineEnvironment(
                repository, "alternates", "object-dir", "quarantine-path");
        for (int i = 0; i < 100; ++i) {
            String quarantineId = registry.register(env1);
            //The environmentId portion of the quarantineId should start from 1
            assertEquals(formatQuarantineId(i + 1), quarantineId);
        }
    }

    @Test
    public void testRegister() {
        GitQuarantineEnvironment env1 = new GitQuarantineEnvironment(
                repository, "alternates", "object-dir", "quarantine-path");
        GitQuarantineEnvironment env2 = new GitQuarantineEnvironment(
                repository, "alternates2", "object-dir2", "quarantine-path2");

        String quarantined1 = registry.register(env1);
        assertEquals(formatQuarantineId(1), quarantined1);
        String quarantined2 = registry.register(env2);
        assertEquals(formatQuarantineId(2), quarantined2);

        //Verify that the quarantined environment is retrievable, but only with the correct ID
        assertEquals(of(env1), registry.get(quarantined1));
        assertEquals(of(env2), registry.get(quarantined2));
        assertEquals(empty(), registry.get(REPO1_ID));
        assertEquals(empty(), registry.get(REPO2_ID));

        Repository quarantined = mock(Repository.class);
        when(quarantined.getParameter(PARAM_QUARANTINE)).thenReturn("2");
        assertEquals(of(env2), registry.get(quarantined));

        //Unregister one environment and revalidate
        registry.unregister(quarantined1);
        assertEquals(empty(), registry.get(quarantined1));
        assertEquals(of(env2), registry.get(quarantined2));
        assertEquals(of(env2), registry.get(quarantined));
    }

    @Test
    public void testUnregisterUsingContextListener() {
        Mutable<String> quarantineId = new MutableObject<>();
        try (Context.CancellableContext context = Context.current().withCancellation()) {
            context.run(() -> {
                GitQuarantineEnvironment env1 = new GitQuarantineEnvironment(
                        repository, "alternates", "object-dir", "quarantine-path");
                quarantineId.setValue(registry.register(env1));

                assertEquals(of(env1), registry.get(quarantineId.getValue()));
            });
        }

        assertEquals(empty(), registry.get(quarantineId.getValue()));
    }

    private static String formatQuarantineId(int environmentId) {
        return REPO1_ID + "?" + PARAM_QUARANTINE + "=" + environmentId;
    }
}