package org.adrianvictor.lib;

import org.adrianvictor.lib.versioning.DefaultVersionedServiceFactory;
import org.adrianvictor.lib.versioning.VersionMatcher;
import org.adrianvictor.lib.versioning.VersionedServiceFactory;
import org.adrianvictor.lib.versioning.VersionedServiceRegistrar;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

// Define dummy interfaces for testing
interface TestService {}
class TestServiceB1_7_3 implements TestService {}
class TestServiceR1_21 implements TestService {}

// Dummy registrar for testing
class DummyRegistrarB1_7_3 implements VersionedServiceRegistrar {
    @Override
    public void register(VersionedServiceFactory factory) {
        factory.register(TestService.class, "b1_7_3", TestServiceB1_7_3::new);
    }
}

class DummyRegistrarR1_21 implements VersionedServiceRegistrar {
    @Override
    public void register(VersionedServiceFactory factory) {
        factory.register(TestService.class, "r1_1", TestServiceR1_21::new); // Registers for r1_1 compat
        factory.register(TestService.class, "r1_16_5", TestServiceR1_21::new); // Registers for r1_16_5 compat
        factory.register(TestService.class, "r1_21", TestServiceR1_21::new); // Registers for r1_21
    }
}


public class DefaultVersionedServiceFactoryTest {

    private VersionMatcher mockVersionMatcher;
    private DefaultVersionedServiceFactory factory;

    @BeforeEach
    void setUp() {
        mockVersionMatcher = mock(VersionMatcher.class);
        factory = new DefaultVersionedServiceFactory(mockVersionMatcher);
    }

    @Test
    void testServiceRegistrationAndRetrievalForB1_7_3() {
        // Mock VersionMatcher to return b1_7_3 suffix
        when(mockVersionMatcher.getServerVersion()).thenReturn("1.7.3");
        when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3");

        // Register the dummy service for b1_7_3
        new DummyRegistrarB1_7_3().register(factory);

        // Retrieve the service
        TestService service = factory.getService(TestService.class);

        // Assert that the correct version is returned
        assertNotNull(service);
        assertTrue(service instanceof TestServiceB1_7_3);
    }

    @Test
    void testServiceRegistrationAndRetrievalForR1_1() {
        // Mock VersionMatcher to return r1_1 suffix for a 1.10.2 server
        when(mockVersionMatcher.getServerVersion()).thenReturn("1.10.2");
        when(mockVersionMatcher.getClassSuffix(eq("1.10.2"))).thenReturn("r1_1");

        // Register the dummy service for R1_21 (which covers r1_1 compat)
        new DummyRegistrarR1_21().register(factory);

        // Retrieve the service
        TestService service = factory.getService(TestService.class);

        // Assert that the correct compatible version is returned
        assertNotNull(service);
        assertTrue(service instanceof TestServiceR1_21);
    }

    @Test
    void testServiceRegistrationAndRetrievalForR1_16_5() {
        // Mock VersionMatcher to return r1_16_5 suffix for a 1.18.1 server
        when(mockVersionMatcher.getServerVersion()).thenReturn("1.18.1");
        when(mockVersionMatcher.getClassSuffix(eq("1.18.1"))).thenReturn("r1_16_5");

        // Register the dummy service for R1_21 (which covers r1_16_5 compat)
        new DummyRegistrarR1_21().register(factory);

        // Retrieve the service
        TestService service = factory.getService(TestService.class);

        // Assert that the correct compatible version is returned
        assertNotNull(service);
        assertTrue(service instanceof TestServiceR1_21);
    }

    @Test
    void testServiceRegistrationAndRetrievalForR1_21() {
        // Mock VersionMatcher to return r1_21 suffix
        when(mockVersionMatcher.getServerVersion()).thenReturn("1.21.0");
        when(mockVersionMatcher.getClassSuffix(eq("1.21.0"))).thenReturn("r1_21");

        // Register the dummy service for r1_21
        new DummyRegistrarR1_21().register(factory);

        // Retrieve the service
        TestService service = factory.getService(TestService.class);

        // Assert that the correct version is returned
        assertNotNull(service);
        assertTrue(service instanceof TestServiceR1_21);
    }

    @Test
    void testNoServiceRegisteredThrowsException() {
        // Mock VersionMatcher to return an unknown suffix
        when(mockVersionMatcher.getServerVersion()).thenReturn("unknown");
        when(mockVersionMatcher.getClassSuffix(anyString())).thenReturn("unknown_version");

        // Attempt to retrieve a service without any registration
        IllegalStateException exception = assertThrows(IllegalStateException.class, () ->
            factory.getService(TestService.class)
        );

        assertTrue(exception.getMessage().contains("No service registered for type"));
    }

    @Test
    void testSpecificServiceNotRegisteredForVersionThrowsException() {
        // Mock VersionMatcher to return b1_7_3 suffix
        when(mockVersionMatcher.getServerVersion()).thenReturn("1.7.3");
        when(mockVersionMatcher.getClassSuffix(eq("1.7.3"))).thenReturn("b1_7_3");

        // Register R1_21 service, but not B1_7_3
        new DummyRegistrarR1_21().register(factory); // R1_21 registrar doesn't register b1_7_3

        // Attempt to retrieve b1_7_3 service
        IllegalStateException exception = assertThrows(IllegalStateException.class, () ->
            factory.getService(TestService.class)
        );

        assertTrue(exception.getMessage().contains("No service registered for type"));
    }
}
