package com.eactive.eai.transformer;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.jdbc.Sql;

import com.eactive.eai.transformer.engine.TransformEngine;
import com.eactive.eai.transformer.function.jpa.TransformJPATestConfiguration;
import com.eactive.eai.transformer.message.Message;
import com.eactive.eai.transformer.message.MessageFactory;
import com.eactive.eai.transformer.message.MessageSetDataException;

/**
 * BytesMessage 필드 validation 영향도 단위테스트
 *
 * 테스트 대상 레이아웃: EAI_UBYTESTYPE_REQ1 (BYTES 타입, 전문길이=35)
 *   - INT_VALUE : TYPE_BYTEARRAY(2113), length=5,  decimal=0
 *   - BIG_VALUE : TYPE_BYTEARRAY(2113), length=10, decimal=5
 *   - STR_VALUE : TYPE_BYTEARRAY(2113), length=20, decimal=0
 *
 * 검증 목적
 *   BytesMessage 필드는 Converter.convert()의 TYPE_BYTEARRAY(2113) 분기를 사용하며,
 *   변경 대상인 TYPE_BIGDECIMAL(2118) + String 분기와 완전히 분리되어 있음을 확인한다.
 *
 * 시나리오
 *   A) setData(byte[]) 경로  — TYPE_BYTEARRAY 분기, 변경 전후 동일
 *   B) setString() 경로       — TYPE_BYTEARRAY 분기, 변경 전후 동일
 *      B-1) 정상 숫자 문자열  → 예외 없음 (bytes로 저장)
 *      B-2) 비숫자 문자열     → 예외 없음 (bytes로 저장, BigDecimal 변환 없음)
 *      B-3) 자릿수 초과 문자열, validation=true  → 예외 없음 (TYPE_BYTEARRAY는 validation 대상 아님)
 *      B-4) 자릿수 초과 문자열, validation=false → 예외 없음
 */
@DataJpaTest
@ContextConfiguration(classes = { TransformJPATestConfiguration.class })
@Sql({
    "/com/eactive/eai/transformer/init_tseaitr06.sql",
    "/com/eactive/eai/transformer/init_bytes_validation.sql"
})
@DisplayName("BytesMessage Validation 영향도 테스트")
class BytesMessageValidationTest {

    @Autowired
    TransformEngine engine;

    // 전문 구조: INT_VALUE(5) + BIG_VALUE(10) + STR_VALUE(20) = 35 bytes
    // BytesMessage는 소수점 없이 숫자만 저장 (decimal=5이면 끝 5자리가 소수부)
    private static final String VALID_RAW =
            "00042"            // INT_VALUE  : 5자리 정수
          + "0012312345"       // BIG_VALUE  : 10자리 (정수부5 + 소수부5, 소수점 제외)
          + "HELLO WORLD         ";  // STR_VALUE  : 20자리 문자열

    @BeforeEach
    void setUp() {
        try {
            if (!engine.isStarted()) {
                engine.start();
            }
        } catch (Exception e) {
            assumeTrue(false, "TransformEngine 시작 실패: " + e.getMessage());
        }
    }

    @AfterEach
    void tearDown() {
        System.clearProperty(SystemKeys.TRANSFROMER_VALIDATION);
        System.clearProperty(SystemKeys.TRANSFROMER_SCIENTIFIC_NORMALIZE);
        SystemKeys.reload();
    }

    // ─────────────────────────────────────────────────────────────────
    // A. setData(byte[]) 경로 — TYPE_BYTEARRAY 분기 직접 사용
    //    Converter TYPE_BIGDECIMAL 분기와 완전히 무관
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("[A] 정상 전문(byte[]) setData → 정상 처리")
    void testSetDataWithValidBytes() {
        byte[] raw = VALID_RAW.getBytes();
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");

        assertDoesNotThrow(() -> msg.setData(raw),
                "정상 전문 byte[] setData는 예외가 발생하지 않아야 합니다");

        byte[] result = (byte[]) msg.getData();
        assertNotNull(result, "getData() 결과가 null이 아니어야 합니다");
        assertArrayEquals(raw, result, "입력 전문과 출력 전문이 동일해야 합니다");
    }

    @Test
    @DisplayName("[A] validation=true 상태에서 byte[] setData → 영향 없음")
    void testSetDataWithValidationEnabled() {
        enableValidation();
        byte[] raw = VALID_RAW.getBytes();
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");

        assertDoesNotThrow(() -> msg.setData(raw),
                "validation=true 설정이 BytesMessage setData(byte[])에 영향을 주지 않아야 합니다");
    }

    // ─────────────────────────────────────────────────────────────────
    // B-1. setString() — 정상 숫자 문자열
    //      TYPE_BYTEARRAY 분기 → String.getBytes() 저장, BigDecimal 변환 없음
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("[B-1] BIG_VALUE에 정상 숫자 문자열 setString → 예외 없음")
    void testSetStringWithValidDecimal() {
        byte[] raw = VALID_RAW.getBytes();
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
        assertDoesNotThrow(() -> msg.setData(raw));

        assertDoesNotThrow(
                () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "1234.12345"),
                "정상 숫자 문자열 setString은 예외가 발생하지 않아야 합니다");
    }

    // ─────────────────────────────────────────────────────────────────
    // B-2. setString() — 비숫자 문자열
    //      TYPE_BYTEARRAY 분기 → String.getBytes() 저장
    //      BigDecimal 변환이 없으므로 NumberFormatException 발생 안 함
    //      (JSONMessage와 다른 동작: JSONMessage는 Converter TYPE_BIGDECIMAL 분기 → 예외)
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("[B-2] BIG_VALUE에 비숫자 문자열 setString → TYPE_BYTEARRAY이므로 예외 없음")
    void testSetStringWithNonNumeric() {
        byte[] raw = VALID_RAW.getBytes();
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
        assertDoesNotThrow(() -> msg.setData(raw));

        assertDoesNotThrow(
                () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "not-a-number"),
                "TYPE_BYTEARRAY 필드는 비숫자 문자열도 bytes로 저장하므로 예외가 발생하지 않아야 합니다");
    }

    // ─────────────────────────────────────────────────────────────────
    // B-3. setString() — 자릿수 초과 문자열 + validation=true
    //      TYPE_BYTEARRAY 분기는 자릿수 검증 자체가 없음
    //      → validation 설정과 무관하게 예외 발생 안 함
    //      ★ 이 동작은 Converter TYPE_BIGDECIMAL 분기 수정 후에도 동일해야 함 ★
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("[B-3] BIG_VALUE 자릿수 초과 + validation=true → TYPE_BYTEARRAY이므로 예외 없음")
    void testSetStringOverflowWithValidationEnabled() {
        enableValidation();
        byte[] raw = VALID_RAW.getBytes();
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
        assertDoesNotThrow(() -> msg.setData(raw));

        // 정수부 5자리(12345) > BIG_VALUE decimal=5 기준 제한(4자리) 초과
        // → TYPE_BIGDECIMAL이었다면 validation 오류, TYPE_BYTEARRAY이므로 통과
        assertDoesNotThrow(
                () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "12345.123456"),
                "TYPE_BYTEARRAY 필드는 validation 대상이 아니므로 자릿수 초과에도 예외가 발생하지 않아야 합니다");
    }

    // ─────────────────────────────────────────────────────────────────
    // B-4. setString() — 자릿수 초과 문자열
    //      setString 경로는 setDataBytes()를 통하지 않으므로
    //      validation 설정과 완전히 무관하게 예외 발생 안 함 (B-3과 결과 동일)
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("[B-4] BIG_VALUE 자릿수 초과 setString → setString 경로는 decimal 검증 대상 아님")
    void testSetStringOverflowIsNotSubjectToDecimalValidation() {
        // setString → setDataBytes() 미경유 → decimal 숫자형 검증 실행 안 됨
        // validation 설정과 무관하게 예외 없음
        byte[] raw = VALID_RAW.getBytes();
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
        assertDoesNotThrow(() -> msg.setData(raw));

        assertDoesNotThrow(
                () -> msg.setString("EAI_UBYTESTYPE_REQ1.BIG_VALUE[0]", "12345.123456"),
                "TYPE_BYTEARRAY setString은 setDataBytes()를 통하지 않으므로 자릿수 초과에도 예외가 발생하지 않아야 합니다");
    }

    // ─────────────────────────────────────────────────────────────────
    // C. decimal > 0 필드 숫자 검증 — 파싱(setData) 시점
    //    BytesMessage는 raw bytes를 위치 기반으로 잘라 저장하므로,
    //    decimal > 0 인 숫자형 필드에 비숫자 bytes가 들어오는 경우를 검증
    //
    //    INVALID_RAW: BIG_VALUE 위치(5~14)에 비숫자 "HELLO_WOLD" 삽입
    //    전문 구조 동일: INT_VALUE(5) + BIG_VALUE(10) + STR_VALUE(20) = 35 bytes
    //
    //    C-1) 비숫자 bytes + validation=true  → MessageSetDataException
    //    C-2) 비숫자 bytes + validation=false → 예외 없음 (기존 동작 유지)
    //    C-3) 정상 숫자 bytes + validation=true → 예외 없음 ([A] 테스트와 동일 경로)
    // ─────────────────────────────────────────────────────────────────

    private static final String INVALID_RAW =
            "00042"            // INT_VALUE  : 5자리 정수 (decimal=0, 검증 대상 아님)
          + "HELLO_WOLD"       // BIG_VALUE  : 비숫자 10자리 (decimal=5 → 숫자여야 함)
          + "HELLO WORLD         ";  // STR_VALUE  : 20자리 문자열 (decimal=0, 검증 대상 아님)

    @Test
    @DisplayName("[C-1] decimal>0 필드에 비숫자 bytes + validation=true → MessageSetDataException")
    void testDecimalFieldNonNumeric_withValidation() {
        // validation 기본값이 true이므로 enableValidation() 호출 불필요
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
        assertThrows(MessageSetDataException.class,
                () -> msg.setData(INVALID_RAW.getBytes()),
                "decimal>0 필드에 비숫자 bytes 입력 시 validation=true이면 MessageSetDataException이 발생해야 합니다");
    }

    @Test
    @DisplayName("[C-2] decimal>0 필드에 비숫자 bytes + validation=false → 예외 없음")
    void testDecimalFieldNonNumeric_withoutValidation() {
        // 기본값이 true이므로 명시적으로 비활성화
        System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "false");
        SystemKeys.reload();
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
        assertDoesNotThrow(
                () -> msg.setData(INVALID_RAW.getBytes()),
                "validation=false 상태에서는 비숫자 bytes도 예외가 발생하지 않아야 합니다");
    }

    @Test
    @DisplayName("[C-3] decimal>0 필드에 정상 숫자 bytes + validation=true → 예외 없음")
    void testDecimalFieldNumeric_withValidation() {
        // validation 기본값이 true이므로 enableValidation() 호출 불필요
        Message msg = MessageFactory.getFactory().getMessage("EAI_UBYTESTYPE_REQ1");
        assertDoesNotThrow(
                () -> msg.setData(VALID_RAW.getBytes()),
                "decimal>0 필드에 정상 숫자 bytes 입력 시 validation=true에서도 예외가 발생하지 않아야 합니다");
    }

    // ─────────────────────────────────────────────────────────────────
    // 유틸 메서드
    // ─────────────────────────────────────────────────────────────────

    private void enableValidation() {
        System.setProperty(SystemKeys.TRANSFROMER_VALIDATION, "true");
        SystemKeys.reload();
    }
}
