package com.eactive.eai.manage.inflow;

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

import java.util.List;

import org.junit.jupiter.api.Test;

import com.eactive.eai.common.inflow.InflowTargetVO;
import com.eactive.eai.common.inflow.dual.DualInflowControlManager;

/**
 * InflowTargetMetricService 단위 테스트.
 *
 * <p>어댑터/인터페이스 테스트는 getDualManager()를 오버라이드하여 DualInflowControlManager 주입.
 */
class InflowTargetMetricServiceTest {

    // -------------------------------------------------------------------------
    // 테스트용 서브클래스 — 어댑터/인터페이스용
    // -------------------------------------------------------------------------

    private static InflowTargetMetricService serviceWith(DualInflowControlManager manager) {
        return new InflowTargetMetricService() {
            @Override
            protected DualInflowControlManager getDualManager() { return manager; }
        };
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    private InflowTargetVO makeVO(String name) {
        InflowTargetVO vo = new InflowTargetVO();
        vo.setName(name);
        vo.setActivate(true);
        return vo;
    }

    private DualInflowControlManager.TargetMetrics makeMetrics(long allowed, long rejPs, long rejTh) {
        DualInflowControlManager.TargetMetrics m = new DualInflowControlManager.TargetMetrics();
        m.allowed.set(allowed);
        m.rejectedPerSecond.set(rejPs);
        m.rejectedThreshold.set(rejTh);
        return m;
    }

    // =========================================================================
    // DualManager null (비활성 환경)
    // =========================================================================

    @Test
    void getAdapterMetric_managerIsNull_returnsNull() {
        assertNull(serviceWith(null).getAdapterMetric("A1"));
    }

    @Test
    void getAllAdapterMetrics_managerIsNull_returnsEmptyList() {
        assertTrue(serviceWith(null).getAllAdapterMetrics().isEmpty());
    }

    @Test
    void resetAdapterMetric_managerIsNull_returnsFalse() {
        assertFalse(serviceWith(null).resetAdapterMetric("A1"));
    }

    @Test
    void resetAllAdapterMetrics_managerIsNull_noException() {
        assertDoesNotThrow(() -> serviceWith(null).resetAllAdapterMetrics());
    }

    @Test
    void getInterfaceMetric_managerIsNull_returnsNull() {
        assertNull(serviceWith(null).getInterfaceMetric("IF1"));
    }

    // =========================================================================
    // 어댑터 — 단일 조회
    // =========================================================================

    @Test
    void getAdapterMetric_notRegistered_returnsNull() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(null);

        assertNull(serviceWith(manager).getAdapterMetric("A1"));
    }

    @Test
    void getAdapterMetric_found_mapsFields() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(makeVO("A1"));
        when(manager.getAdapterMetrics("A1")).thenReturn(makeMetrics(900, 60, 40));

        TargetMetricDTO dto = serviceWith(manager).getAdapterMetric("A1");

        assertNotNull(dto);
        assertEquals("A1",      dto.getTargetId());
        assertEquals("ADAPTER", dto.getTargetType());
        assertTrue(dto.isActivate());
        assertEquals(900, dto.getAllowed());
        assertEquals(60,  dto.getRejectedPerSecond());
        assertEquals(40,  dto.getRejectedThreshold());
        assertEquals(1000, dto.getTotalRequests());
    }

    @Test
    void getAdapterMetric_noMetricsYet_returnsZeroCounters() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(makeVO("A1"));
        when(manager.getAdapterMetrics("A1")).thenReturn(null);

        TargetMetricDTO dto = serviceWith(manager).getAdapterMetric("A1");

        assertNotNull(dto);
        assertEquals(0, dto.getAllowed());
        assertEquals(0, dto.getTotalRequests());
        assertEquals(0.0, dto.getRejectRatio(), 0.001);
    }

    @Test
    void getAdapterMetric_rejectRatioCalculation() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(makeVO("A1"));
        // allowed=90, rejPs=5, rejTh=5 → reject=10/100 = 10.00%
        when(manager.getAdapterMetrics("A1")).thenReturn(makeMetrics(90, 5, 5));

        TargetMetricDTO dto = serviceWith(manager).getAdapterMetric("A1");

        assertEquals(10.0, dto.getRejectRatio(), 0.01);
    }

    @Test
    void getAdapterMetric_zeroTotal_rejectRatioIsZero() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(makeVO("A1"));
        when(manager.getAdapterMetrics("A1")).thenReturn(makeMetrics(0, 0, 0));

        TargetMetricDTO dto = serviceWith(manager).getAdapterMetric("A1");

        assertEquals(0.0, dto.getRejectRatio(), 0.0001);
    }

    // =========================================================================
    // 어댑터 — 전체 조회
    // =========================================================================

    @Test
    void getAllAdapterMetrics_twoEntries_returnsBoth() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterAllKeys()).thenReturn(new String[]{"A1", "A2"});
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(makeVO("A1"));
        when(manager.getAdapterInflowThreashold("A2")).thenReturn(makeVO("A2"));
        when(manager.getAdapterMetrics("A1")).thenReturn(makeMetrics(10, 0, 0));
        when(manager.getAdapterMetrics("A2")).thenReturn(makeMetrics(20, 5, 0));

        List<TargetMetricDTO> result = serviceWith(manager).getAllAdapterMetrics();

        assertEquals(2, result.size());
        assertEquals("A1", result.get(0).getTargetId());
        assertEquals("A2", result.get(1).getTargetId());
    }

    @Test
    void getAllAdapterMetrics_noKeys_returnsEmptyList() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterAllKeys()).thenReturn(new String[]{});

        assertTrue(serviceWith(manager).getAllAdapterMetrics().isEmpty());
    }

    @Test
    void getAllAdapterMetrics_notRegisteredSkipped() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterAllKeys()).thenReturn(new String[]{"A1", "GHOST"});
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(makeVO("A1"));
        when(manager.getAdapterInflowThreashold("GHOST")).thenReturn(null);
        when(manager.getAdapterMetrics("A1")).thenReturn(null);

        List<TargetMetricDTO> result = serviceWith(manager).getAllAdapterMetrics();

        assertEquals(1, result.size());
        assertEquals("A1", result.get(0).getTargetId());
    }

    // =========================================================================
    // 어댑터 — reset
    // =========================================================================

    @Test
    void resetAdapterMetric_notRegistered_returnsFalse() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(null);

        assertFalse(serviceWith(manager).resetAdapterMetric("A1"));
        verify(manager, never()).resetAdapterMetrics(any());
    }

    @Test
    void resetAdapterMetric_success_returnsTrueAndCallsManager() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getAdapterInflowThreashold("A1")).thenReturn(makeVO("A1"));

        assertTrue(serviceWith(manager).resetAdapterMetric("A1"));
        verify(manager).resetAdapterMetrics("A1");
    }

    @Test
    void resetAllAdapterMetrics_callsManager() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);

        serviceWith(manager).resetAllAdapterMetrics();

        verify(manager).resetAllAdapterMetrics();
    }

    // =========================================================================
    // 인터페이스 — 단일 조회
    // =========================================================================

    @Test
    void getInterfaceMetric_found_mapsFields() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getInterfaceInflowThreashold("IF1")).thenReturn(makeVO("IF1"));
        when(manager.getInterfaceMetrics("IF1")).thenReturn(makeMetrics(50, 10, 5));

        TargetMetricDTO dto = serviceWith(manager).getInterfaceMetric("IF1");

        assertNotNull(dto);
        assertEquals("IF1",       dto.getTargetId());
        assertEquals("INTERFACE", dto.getTargetType());
        assertEquals(50,  dto.getAllowed());
        assertEquals(65,  dto.getTotalRequests());
    }

    @Test
    void getInterfaceMetric_notRegistered_returnsNull() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getInterfaceInflowThreashold("IF1")).thenReturn(null);

        assertNull(serviceWith(manager).getInterfaceMetric("IF1"));
    }

    @Test
    void resetInterfaceMetric_success_returnsTrueAndCallsManager() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getInterfaceInflowThreashold("IF1")).thenReturn(makeVO("IF1"));

        assertTrue(serviceWith(manager).resetInterfaceMetric("IF1"));
        verify(manager).resetInterfaceMetrics("IF1");
    }

    @Test
    void resetInterfaceMetric_notRegistered_returnsFalse() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        when(manager.getInterfaceInflowThreashold("IF1")).thenReturn(null);

        assertFalse(serviceWith(manager).resetInterfaceMetric("IF1"));
    }

    @Test
    void resetAllInterfaceMetrics_callsManager() {
        DualInflowControlManager manager = mock(DualInflowControlManager.class);
        serviceWith(manager).resetAllInterfaceMetrics();
        verify(manager).resetAllInterfaceMetrics();
    }
}
