package com.eactive.eai.manage.session;

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

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.ResponseEntity;
import org.springframework.test.util.ReflectionTestUtils;

import com.eactive.eai.common.session.CacheInfoVO;

/**
 * SessionCacheManageController 단위 테스트.
 *
 * <p>Spring MVC를 기동하지 않고 컨트롤러를 직접 인스턴스화하여
 * 응답 구조(success, data, count, message)를 검증한다.
 */
class SessionCacheManageControllerTest {

    private SessionCacheManageController controller;
    private SessionCacheManageService mockService;

    @BeforeEach
    void setUp() {
        controller  = new SessionCacheManageController();
        mockService = mock(SessionCacheManageService.class);
        ReflectionTestUtils.setField(controller, "sessionCacheManageService", mockService);
    }

    // -------------------------------------------------------------------------
    // Helper
    // -------------------------------------------------------------------------

    private CacheInfoVO makeCacheInfo(String name) {
        CacheInfoVO vo = new CacheInfoVO();
        vo.setName(name);
        vo.setSize(3);
        vo.setKeys(Arrays.asList("a", "b", "c"));
        vo.setTimeToLive(60000);
        return vo;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> body(ResponseEntity<?> response) {
        return (Map<String, Object>) response.getBody();
    }

    // =========================================================================
    // 캐시 요약 / 마스터 인스턴스
    // =========================================================================

    @Test
    @DisplayName("getCacheStatus() — success=true, data에 원문 그대로 반환")
    void getCacheStatus_returnsRawStatusString() {
        when(mockService.getCacheStatus()).thenReturn("<Caches type=\"Ignite\"></Caches>");

        Map<String, Object> body = body(controller.getCacheStatus());

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("<Caches type=\"Ignite\"></Caches>", body.get("data"));
    }

    @Test
    @DisplayName("getMasterInstId() — success=true, data에 인스턴스명 반환")
    void getMasterInstId_returnsInstanceName() {
        when(mockService.getMasterInstId()).thenReturn("SERVER01");

        Map<String, Object> body = body(controller.getMasterInstId());

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("SERVER01", body.get("data"));
    }

    // =========================================================================
    // 개별 캐시 조회
    // =========================================================================

    @Test
    @DisplayName("getLoginCache() — success=true, CacheInfoVO 반환")
    void getLoginCache_returnsCacheInfo() {
        CacheInfoVO vo = makeCacheInfo("LoginCache");
        when(mockService.getLoginCache()).thenReturn(vo);

        ResponseEntity<?> response = controller.getLoginCache();
        Map<String, Object> body = body(response);

        assertEquals(200, response.getStatusCodeValue());
        assertEquals(Boolean.TRUE, body.get("success"));
        assertSame(vo, body.get("data"));
    }

    @Test
    @DisplayName("getHttpLoginCache() — success=true")
    void getHttpLoginCache_returnsCacheInfo() {
        CacheInfoVO vo = makeCacheInfo("HttpLoginCache");
        when(mockService.getHttpLoginCache()).thenReturn(vo);

        assertSame(vo, body(controller.getHttpLoginCache()).get("data"));
    }

    @Test
    @DisplayName("getEvictMasterCache() — success=true")
    void getEvictMasterCache_returnsCacheInfo() {
        CacheInfoVO vo = makeCacheInfo("EvictMasterCache");
        when(mockService.getEvictMasterCache()).thenReturn(vo);

        assertSame(vo, body(controller.getEvictMasterCache()).get("data"));
    }

    @Test
    @DisplayName("getTerminalCache() — success=true")
    void getTerminalCache_returnsCacheInfo() {
        CacheInfoVO vo = makeCacheInfo("TerminalCache");
        when(mockService.getTerminalCache()).thenReturn(vo);

        assertSame(vo, body(controller.getTerminalCache()).get("data"));
    }

    @Test
    @DisplayName("getConvertUserIdCache() — success=true")
    void getConvertUserIdCache_returnsCacheInfo() {
        CacheInfoVO vo = makeCacheInfo("ConvertUserIdCache");
        when(mockService.getConvertUserIdCache()).thenReturn(vo);

        assertSame(vo, body(controller.getConvertUserIdCache()).get("data"));
    }

    @Test
    @DisplayName("getSocketCache() — success=true")
    void getSocketCache_returnsCacheInfo() {
        CacheInfoVO vo = makeCacheInfo("SocketCache");
        when(mockService.getSocketCache()).thenReturn(vo);

        assertSame(vo, body(controller.getSocketCache()).get("data"));
    }

    // =========================================================================
    // 전체 캐시 목록
    // =========================================================================

    @Test
    @DisplayName("getAllCaches() — 목록과 개수 반환")
    void getAllCaches_returnsListAndCount() {
        List<CacheInfoVO> list = Arrays.asList(makeCacheInfo("A"), makeCacheInfo("B"));
        when(mockService.getAllCaches()).thenReturn(list);

        Map<String, Object> body = body(controller.getAllCaches());

        assertEquals(Boolean.TRUE, body.get("success"));
        assertSame(list, body.get("data"));
        assertEquals(2, body.get("count"));
    }

    @Test
    @DisplayName("getAllCaches() — 빈 목록이면 count=0")
    void getAllCaches_emptyList_countZero() {
        when(mockService.getAllCaches()).thenReturn(Arrays.asList());

        Map<String, Object> body = body(controller.getAllCaches());

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals(0, body.get("count"));
    }

    // =========================================================================
    // OutboundAccessToken 캐시 — 지원/미지원 백엔드
    // =========================================================================

    @Test
    @DisplayName("getOutboundAccessTokenCache() — 지원 시 success=true, CacheInfoVO 반환")
    void getOutboundAccessTokenCache_supported_returnsCacheInfo() {
        CacheInfoVO vo = makeCacheInfo("OutBoundAccessToken");
        when(mockService.getOutboundAccessTokenCache()).thenReturn(vo);

        Map<String, Object> body = body(controller.getOutboundAccessTokenCache());

        assertEquals(Boolean.TRUE, body.get("success"));
        assertSame(vo, body.get("data"));
    }

    @Test
    @DisplayName("getOutboundAccessTokenCache() — Ehcache 등 미지원 백엔드는 500이 아니라 success=false로 응답")
    void getOutboundAccessTokenCache_unsupported_successFalseNotServerError() {
        when(mockService.getOutboundAccessTokenCache()).thenThrow(new UnsupportedOperationException());

        ResponseEntity<?> response = controller.getOutboundAccessTokenCache();
        Map<String, Object> body = body(response);

        assertEquals(200, response.getStatusCodeValue());
        assertEquals(Boolean.FALSE, body.get("success"));
        assertNotNull(body.get("message"));
        assertFalse(body.containsKey("data"));
    }
}
