package de.renew.plugin;

import java.lang.module.Configuration;
import java.lang.module.ModuleFinder;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Set;

import org.junit.jupiter.api.Disabled;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;

public class ServiceLookupInfrastructureTest {
    /**
     * <pre>
     * module testModule {
     *   provides Number with MyServiceProvider,
     *     AnotherServiceProvider;
     * }
     * </pre>
     */
    private static final String TEST_MODULE_NAME = "testModule";

    private static ServiceLookupInfrastructure _instance;

    //@BeforeAll
    public static void setUp() {
        _instance = ServiceLookupInfrastructure.getInstance();
    }

    @Disabled
    void testGetFirst() throws ServiceLookupException {
        // Load the test module containing the service provider
        URI moduleUrl = loadJar();
        ModuleLayer layer = createLayerForModule(moduleUrl);

        // Add the service providers of the test module
        _instance.addServicesOfModules(layer.modules());

        // Example provider for the abstract class Number
        // The first found provider should be: MyServiceProvider
        // and not AnotherServiceProvider
        Number provider = _instance.getFirstServiceProvider(Number.class);

        String output = provider.toString();
        assertEquals(
            "It works", output, "Output of test service provider doesn't match expected result");

        // Cleanup the services
        _instance.removeServicesOfModules(layer.modules());
        assertThrows(
            ServiceLookupException.class, () -> _instance.getFirstServiceProvider(Number.class),
            "The test service should have been removed");
    }

    @Disabled
    void testGetAll() {
        // Load the test module containing the service provider
        URI moduleUrl = loadJar();
        ModuleLayer layer = createLayerForModule(moduleUrl);

        // Add the service providers of the test module
        _instance.addServicesOfModules(layer.modules());

        // Example provider for the abstract class Number
        // Should find two providers: MyServiceProvider and AnotherServiceProvider
        Collection<Number> provider = _instance.getAllServiceProvider(Number.class);
        assertEquals(
            2, provider.size(),
            "There should only be 2 service providers (found: " + provider.size() + ")");

        // Cleanup the services
        _instance.removeServicesOfModules(layer.modules());
        assertEquals(
            0, _instance.getAllServiceProvider(Number.class).size(),
            "The test services should have been removed");
    }

    private URI loadJar() {
        try {
            return getClass().getResource("/" + TEST_MODULE_NAME + ".jar").toURI();
        } catch (URISyntaxException e) {
            fail("Couldn't load the module jar for this test");
            return null; // this will never be executed
        }
    }

    private ModuleLayer createLayerForModule(URI path) {
        Path modulePath = Path.of(path);
        ModuleFinder finder = ModuleFinder.of(modulePath);

        ModuleLayer currentLayer = ModuleLayer.boot();
        Configuration configuration = currentLayer.configuration()
            .resolve(finder, ModuleFinder.of(), Set.of(TEST_MODULE_NAME));

        ClassLoader loader = getClass().getClassLoader();
        return currentLayer.defineModulesWithOneLoader(configuration, loader);
    }
}
