package com.atlassian.bitbucket.mesh.boot;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.AfterParam;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.mock.env.MockEnvironment;

import java.util.Arrays;
import java.util.Collection;

import static com.atlassian.bitbucket.mesh.boot.NettyConfigApplicationListener.getMaxDirectMemory;
import static java.lang.Long.valueOf;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.springframework.util.unit.DataSize.parse;

@RunWith(Parameterized.class)
public class NettyConfigApplicationListenerTest {

    @Parameter
    public String grpcMaxDirectMemory;
    @Parameter(1)
    public String grpcReservedDirectMemory;
    @Parameter(2)
    public Long nettyMaxDirectMemoryConfigured;
    @Parameter(3)
    public Long nettyMaxDirectMemorySet;

    private NettyConfigApplicationListener underTest;

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][]{
                {"-1", "50MB", null, getMaxDirectMemory() - parse("50MB").toBytes()},
                {"100MB", "50MB", null, parse("100MB").toBytes()},
                {"512GB", "1024GB", null, getMaxDirectMemory() - parse("50MB").toBytes()},
                {"-1", "50MB", parse("30MB").toBytes(), parse("30MB").toBytes()},
                {"150MB", "100MB", parse("30MB").toBytes(), parse("30MB").toBytes()},
                {"-1", "150MB", parse("30GB").toBytes(), getMaxDirectMemory() - parse("150MB").toBytes()},
                {"150MB", "100MB", parse("30GB").toBytes(), parse("150MB").toBytes()}
        });
    }

    @AfterParam
    public static void teardown() {
        System.clearProperty("io.netty.maxDirectMemory");
    }

    @Test
    public void testNettyMaxDirectMemoryConfiguredCorrectly() {
        underTest = new NettyConfigApplicationListener();

        underTest.onApplicationEvent(prepareApplicationEnvironment(grpcMaxDirectMemory, grpcReservedDirectMemory, nettyMaxDirectMemoryConfigured));

        assertEquals(nettyMaxDirectMemorySet, valueOf(System.getProperty("io.netty.maxDirectMemory")));
    }

    private ApplicationEnvironmentPreparedEvent prepareApplicationEnvironment(String grpcMaxDirectMemory,
                                                                              String grpcReservedDirectMemory,
                                                                              Long nettyMaxDirectMemory) {
        MockEnvironment env = new MockEnvironment()
                .withProperty("grpc.direct-memory.max", grpcMaxDirectMemory)
                .withProperty("grpc.direct-memory.reserved", grpcReservedDirectMemory);
        if (nettyMaxDirectMemory != null) {
            env.setProperty("io.netty.maxDirectMemory", String.valueOf(nettyMaxDirectMemory));
        }
        ApplicationEnvironmentPreparedEvent e = new ApplicationEnvironmentPreparedEvent(mock(ConfigurableBootstrapContext.class),
                mock(SpringApplication.class), null, env);

        return e;
    }
}