package com.eactive.eai.manage.tools;

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

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;

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

    private ToolsController controller;
    private ToolsService mockService;

    @BeforeEach
    void setUp() {
        controller  = new ToolsController();
        mockService = mock(ToolsService.class);
        ReflectionTestUtils.setField(controller, "toolsService", mockService);
    }

    private ToolsTextRequestDTO request(String text, String charset) {
        ToolsTextRequestDTO dto = new ToolsTextRequestDTO();
        dto.setText(text);
        dto.setCharset(charset);
        return dto;
    }

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

    // =========================================================================
    // base64
    // =========================================================================

    @Test
    @DisplayName("base64Encode() — 정상 시 success=true, data에 결과 반환")
    void base64Encode_success_returnsData() {
        when(mockService.base64Encode("hello", null)).thenReturn("aGVsbG8=");

        ResponseEntity<?> response = controller.base64Encode(request("hello", null));
        Map<String, Object> body = body(response);

        assertEquals(200, response.getStatusCodeValue());
        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("aGVsbG8=", body.get("data"));
        assertFalse(body.containsKey("message"));
    }

    @Test
    @DisplayName("base64Decode() — 잘못된 입력이면 success=false, message 포함 (500 아님)")
    void base64Decode_serviceThrows_successFalseWithMessage() {
        when(mockService.base64Decode("not-valid!!", null))
                .thenThrow(new IllegalArgumentException("Illegal base64 character"));

        ResponseEntity<?> response = controller.base64Decode(request("not-valid!!", null));
        Map<String, Object> body = body(response);

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

    // =========================================================================
    // hex
    // =========================================================================

    @Test
    @DisplayName("hexEncode() — 정상 시 success=true, data에 결과 반환")
    void hexEncode_success_returnsData() {
        when(mockService.hexEncode("hello", null)).thenReturn("68656C6C6F");

        Map<String, Object> body = body(controller.hexEncode(request("hello", null)));

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

    @Test
    @DisplayName("hexDecode() — 잘못된 Hex 입력이면 success=false, message 포함")
    void hexDecode_serviceThrows_successFalseWithMessage() {
        when(mockService.hexDecode("ZZ", null)).thenThrow(new IllegalArgumentException("invalid hex"));

        Map<String, Object> body = body(controller.hexDecode(request("ZZ", null)));

        assertEquals(Boolean.FALSE, body.get("success"));
        assertNotNull(body.get("message"));
    }

    @Test
    @DisplayName("hexEncode() — charset 파라미터를 서비스에 그대로 위임")
    void hexEncode_withCharset_delegatesToService() {
        when(mockService.hexEncode("한글", "EUC-KR")).thenReturn("C7D1B1DB");

        Map<String, Object> body = body(controller.hexEncode(request("한글", "EUC-KR")));

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("C7D1B1DB", body.get("data"));
        verify(mockService).hexEncode("한글", "EUC-KR");
    }

    // =========================================================================
    // damo
    // =========================================================================

    @Test
    @DisplayName("damoEncrypt() — 정상 시 success=true, data에 암호문 반환")
    void damoEncrypt_success_returnsData() {
        when(mockService.damoEncrypt("홍길동")).thenReturn("ENC(홍길동)");

        Map<String, Object> body = body(controller.damoEncrypt(request("홍길동", null)));

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("ENC(홍길동)", body.get("data"));
    }

    @Test
    @DisplayName("damoDecrypt() — 정상 시 success=true, data에 평문 반환")
    void damoDecrypt_success_returnsData() {
        when(mockService.damoDecrypt("ENC(홍길동)")).thenReturn("홍길동");

        Map<String, Object> body = body(controller.damoDecrypt(request("ENC(홍길동)", null)));

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("홍길동", body.get("data"));
    }

    @Test
    @DisplayName("damoDecrypt() — 서비스 예외 발생 시 success=false, message 포함 (500 아님)")
    void damoDecrypt_serviceThrows_successFalseWithMessage() {
        when(mockService.damoDecrypt("broken")).thenThrow(new RuntimeException("DAMO decrypt failed"));

        ResponseEntity<?> response = controller.damoDecrypt(request("broken", null));
        Map<String, Object> body = body(response);

        assertEquals(200, response.getStatusCodeValue());
        assertEquals(Boolean.FALSE, body.get("success"));
        assertEquals("DAMO decrypt failed", body.get("message"));
    }

    // =========================================================================
    // encryption-manager (EncryptionManager.encryptDBData/decryptDBData 확인용)
    // =========================================================================

    @Test
    @DisplayName("encryptionManagerEncrypt() — 정상 시 success=true, data에 결과 반환")
    void encryptionManagerEncrypt_success_returnsData() {
        when(mockService.encryptionManagerEncrypt("홍길동")).thenReturn("ENCDB(홍길동)");

        Map<String, Object> body = body(controller.encryptionManagerEncrypt(request("홍길동", null)));

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("ENCDB(홍길동)", body.get("data"));
    }

    @Test
    @DisplayName("encryptionManagerEncrypt() — encryptYN=N 등으로 원문 그대로 반환되어도 success=true")
    void encryptionManagerEncrypt_passthrough_stillSuccess() {
        when(mockService.encryptionManagerEncrypt("홍길동")).thenReturn("홍길동");

        Map<String, Object> body = body(controller.encryptionManagerEncrypt(request("홍길동", null)));

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("홍길동", body.get("data"));
    }

    @Test
    @DisplayName("encryptionManagerDecrypt() — 정상 시 success=true, data에 결과 반환")
    void encryptionManagerDecrypt_success_returnsData() {
        when(mockService.encryptionManagerDecrypt("ENCDB(홍길동)")).thenReturn("홍길동");

        Map<String, Object> body = body(controller.encryptionManagerDecrypt(request("ENCDB(홍길동)", null)));

        assertEquals(Boolean.TRUE, body.get("success"));
        assertEquals("홍길동", body.get("data"));
    }

    @Test
    @DisplayName("encryptionManagerStatus() — 현재 encryptYN/솔루션명/활성화 여부 반환")
    void encryptionManagerStatus_returnsCurrentConfig() {
        EncryptionManagerStatusDTO status = new EncryptionManagerStatusDTO();
        status.setEncryptYN("Y");
        status.setDbEncryptSolutionName("DAMO");
        status.setEncryptEnabled(true);
        when(mockService.encryptionManagerStatus()).thenReturn(status);

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

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

    @Test
    @DisplayName("encryptionManagerStatus() — 서비스 예외 발생 시 success=false, message 포함")
    void encryptionManagerStatus_serviceThrows_successFalseWithMessage() {
        when(mockService.encryptionManagerStatus()).thenThrow(new IllegalStateException("EncryptionManager not started"));

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

        assertEquals(Boolean.FALSE, body.get("success"));
        assertEquals("EncryptionManager not started", body.get("message"));
    }

    // =========================================================================
    // version
    // =========================================================================

    @Test
    @DisplayName("getVersionInfo() — 정상 시 success=true, data에 VersionInfoDTO 반환")
    void getVersionInfo_success_returnsData() {
        VersionInfoDTO versionInfo = new VersionInfoDTO();
        versionInfo.setVersion("4.5.1-SNAPSHOT");
        versionInfo.setBuildTime("2026-07-22 10:00:00");
        when(mockService.getVersionInfo()).thenReturn(versionInfo);

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

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

    @Test
    @DisplayName("getVersionInfo() — version.info 파일이 없으면 success=false, message 포함 (500 아님)")
    void getVersionInfo_fileMissing_successFalseWithMessage() {
        when(mockService.getVersionInfo()).thenReturn(null);

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

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

    @Test
    @DisplayName("getVersionInfo() — 서비스 예외 발생 시 success=false, message 포함")
    void getVersionInfo_serviceThrows_successFalseWithMessage() {
        when(mockService.getVersionInfo()).thenThrow(new RuntimeException("version.info 읽기 실패"));

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

        assertEquals(Boolean.FALSE, body.get("success"));
        assertEquals("version.info 읽기 실패", body.get("message"));
    }
}
