package dev.lions.unionflow.server.payment.orchestration;

import dev.lions.unionflow.server.api.payment.PaymentProvider;
import jakarta.enterprise.inject.Instance;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class PaymentProviderRegistryTest {

    private PaymentProviderRegistry registry;
    private PaymentProvider waveProvider;
    private PaymentProvider momoProvider;

    @BeforeEach
    @SuppressWarnings("unchecked")
    void setUp() throws Exception {
        registry = new PaymentProviderRegistry();

        waveProvider = mock(PaymentProvider.class);
        when(waveProvider.getProviderCode()).thenReturn("WAVE");

        momoProvider = mock(PaymentProvider.class);
        when(momoProvider.getProviderCode()).thenReturn("MTN_MOMO");

        // Use thenAnswer so spliterator() returns a fresh Spliterator each call
        // (spliterators are single-use)
        Instance<PaymentProvider> instance = mock(Instance.class);
        List<PaymentProvider> providerList = List.of(waveProvider, momoProvider);
        when(instance.spliterator()).thenAnswer(inv -> providerList.spliterator());

        Field f = PaymentProviderRegistry.class.getDeclaredField("providers");
        f.setAccessible(true);
        f.set(registry, instance);
    }

    @Test
    void get_returnsMatchingProvider() {
        assertThat(registry.get("WAVE")).isEqualTo(waveProvider);
        assertThat(registry.get("MTN_MOMO")).isEqualTo(momoProvider);
    }

    @Test
    void get_isCaseInsensitive() {
        assertThat(registry.get("wave")).isEqualTo(waveProvider);
        assertThat(registry.get("mtn_momo")).isEqualTo(momoProvider);
        assertThat(registry.get("Wave")).isEqualTo(waveProvider);
    }

    @Test
    void get_unknownCode_throwsUnsupportedOperationException() {
        assertThatThrownBy(() -> registry.get("UNKNOWN_PROVIDER"))
                .isInstanceOf(UnsupportedOperationException.class)
                .hasMessageContaining("UNKNOWN_PROVIDER");
    }

    @Test
    void getAll_returnsAllProviders() {
        List<PaymentProvider> all = registry.getAll();
        assertThat(all).hasSize(2);
        assertThat(all).contains(waveProvider, momoProvider);
    }

    @Test
    void getAvailableCodes_returnsAllCodes() {
        List<String> codes = registry.getAvailableCodes();
        assertThat(codes).contains("WAVE", "MTN_MOMO");
    }
}
