package com.eactive.eai.custom.message;

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

import java.lang.reflect.Field;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.ApplicationContext;

import com.eactive.eai.common.monitor.EAIServiceMonitor;
import com.eactive.eai.common.property.PropManager;
import com.eactive.eai.common.server.EAIServerManager;
import com.eactive.eai.common.util.ApplicationContextProvider;
import com.eactive.eai.message.StandardItem;
import com.eactive.eai.message.StandardMessage;
import com.eactive.eai.message.manager.StandardMessageManager;
import com.eactive.eai.message.parser.JsonReader;
import com.eactive.eai.message.parser.StandardReader;
import com.eactive.eai.message.service.InterfaceMapper;

import static org.mockito.ArgumentMatchers.anyString;

/**
 * DJBank 표준전문 FLAT 직렬화/파싱 흐름 통합 테스트
 *
 * 검증 시나리오:
 *  1. FlatReader 등록 확인
 *  2. 비ERP 요청 FLAT 왕복 — HEAD 보존, EZDATA/MSG 바이트 미포함
 *  3. ERP 요청 FLAT 왕복 — EZDATA 포함 보존
 *  4. 오류 응답 FLAT 왕복 — MSG EM 포함 보존
 *  5. 정상 응답 FLAT 왕복 — MSG NM 포함 보존
 *  6. 거래로그 시나리오 — FLAT 바이트 길이 검증 (조건부 블록 포함 여부)
 *
 * 핵심 버그 (EZDATA FLAT 오류):
 *  JSON 파싱 시 JSON에 없는 조건부 블록(EZDATA, MSG)은 isHidden=false 로 남는다.
 *  toByteArray() GROUP 케이스에서 size=0 이면서 refPath/refValue 가 있는 경우
 *  isHidden 체크만으로는 불충분 → FLAT에 비활성 블록 바이트가 잘못 포함된다.
 *
 *  수정: toByteArray() / getBytesDataLength() GROUP 케이스를
 *        toJson() 과 동일하게 size==0 이면 무조건 skip 으로 변경
 *        (StandardItem.java 두 곳)
 */
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class StandardMessageFlatFlowTest {

    private static final String TEST_GUID =
            "20260420" + "120000" + "000" + "EAI" + "01" + "123456789012" + "000001";
    // 8+6+3+3+2+12+6 = 40자

    private static final String TEST_IF_ID   = "IF_DJBTEST_FLAT01";
    private static final String TEST_TX_ID   = "TXTEST000003";
    private static final String FLAT_CHARSET = "euc-kr";

    /** 비ERP 요청 JSON — dman_rspn_dvcd=S, corp_tlwn_virt_brcd 미설정 */
    private static final String NON_ERP_REQ_JSON =
        "{"
        + "\"HEAD\":{"
        +   "\"nxgn_stnd_idfr\":\"JERA\","
        +   "\"guid\":\"" + TEST_GUID + "\","
        +   "\"guid_prgs_no\":\"1\","
        +   "\"stnd_mesg_ver\":\"R10\","
        +   "\"ortr_guid\":\"" + TEST_GUID + "\","
        +   "\"dman_rspn_dvcd\":\"S\","
        +   "\"if_id\":\"" + TEST_IF_ID + "\","
        +   "\"procs_rslt_dvcd\":\"S\","
        +   "\"tx_id\":\"" + TEST_TX_ID + "\","
        +   "\"chnl_tycd\":\"EAI\","
        +   "\"hmab_dvcd\":\"1\","
        +   "\"trnm_sys_dvcd\":\"OPA\","
        +   "\"frst_trnm_sys_dvcd\":\"OPA\","
        +   "\"sys_env_dvcd\":\"D\","
        +   "\"frst_mesg_dman_dt\":\"20260420\","
        +   "\"frst_mesg_dman_time\":\"120000000\""
        + "},"
        + "\"DATA\":{"
        +   "\"data_dvcd\":\"IO\","
        +   "\"data_scop_len\":\"00000037\","
        +   "\"BIZ_DATA\":{\"acct_no\":\"123456789\",\"amt\":\"100000\"}"
        + "}"
        + "}";

    /** ERP 요청 JSON — corp_tlwn_virt_brcd=ERP, EZDATA 포함 */
    private static final String ERP_REQ_JSON =
        "{"
        + "\"HEAD\":{"
        +   "\"nxgn_stnd_idfr\":\"JERA\","
        +   "\"guid\":\"" + TEST_GUID + "\","
        +   "\"guid_prgs_no\":\"1\","
        +   "\"stnd_mesg_ver\":\"R10\","
        +   "\"ortr_guid\":\"" + TEST_GUID + "\","
        +   "\"dman_rspn_dvcd\":\"S\","
        +   "\"if_id\":\"" + TEST_IF_ID + "\","
        +   "\"procs_rslt_dvcd\":\"S\","
        +   "\"tx_id\":\"" + TEST_TX_ID + "\","
        +   "\"chnl_tycd\":\"EAI\","
        +   "\"hmab_dvcd\":\"1\","
        +   "\"corp_tlwn_virt_brcd\":\"ERP\","
        +   "\"trnm_sys_dvcd\":\"OPA\","
        +   "\"frst_trnm_sys_dvcd\":\"OPA\","
        +   "\"sys_env_dvcd\":\"D\","
        +   "\"frst_mesg_dman_dt\":\"20260420\","
        +   "\"frst_mesg_dman_time\":\"120000000\""
        + "},"
        + "\"EZDATA\":{"
        +   "\"login_mhod_dvcd\":\"1\","
        +   "\"elfn_bnkn_dvcd\":\"01\","
        +   "\"usr_no\":\"USR0000000000001\","
        +   "\"rgsr_id\":\"REG_ID_000000001\","
        +   "\"secu_media_dvcd\":\"00\","
        +   "\"secu_media_sta_dvcd\":\"00\","
        +   "\"brll_srcd_yn\":\"N\","
        +   "\"next_data_exis_yn\":\"N\","
        +   "\"page_no\":\"00001\","
        +   "\"page_size\":\"00020\","
        +   "\"dup_tx_yn\":\"N\""
        + "},"
        + "\"DATA\":{"
        +   "\"data_dvcd\":\"IO\","
        +   "\"data_scop_len\":\"00000037\","
        +   "\"BIZ_DATA\":{\"acct_no\":\"987654321\",\"amt\":\"500000\"}"
        + "}"
        + "}";

    private StandardMessageManager       manager;
    private InterfaceMapper              mapper;
    private StandardReader               flatReader;
    private JsonReader                   jsonReader;
    private StandardMessageCoordinatorDJB coordinator;

    @BeforeAll
    void setUpAll() throws Exception {
        PropManager mockPropManager = Mockito.mock(PropManager.class);
        Mockito.when(mockPropManager.getProperty(anyString(), anyString(), anyString()))
                .thenAnswer(new Answer<String>() {
                    public String answer(InvocationOnMock inv) { return (String) inv.getArguments()[2]; }
                });

        EAIServerManager mockServer = Mockito.mock(EAIServerManager.class);
        Mockito.when(mockServer.isPEAIServer()).thenReturn(false);
        Mockito.when(mockServer.isSEAIServer()).thenReturn(false);
        Mockito.when(mockServer.isMCI()).thenReturn(false);
        Mockito.when(mockServer.getLocalServerName()).thenReturn("TEST_SERVER");
        Mockito.when(mockServer.getGroupInstId()).thenReturn("TEST_INST");

        EAIServiceMonitor mockMonitor = Mockito.mock(EAIServiceMonitor.class);
        Mockito.when(mockMonitor.isTimeOutCodes(anyString())).thenReturn(false);
        Mockito.when(mockMonitor.isBizErrorCodes(anyString())).thenReturn(false);

        ApplicationContext mockCtx = Mockito.mock(ApplicationContext.class);
        Mockito.when(mockCtx.getBean(EAIServerManager.class)).thenReturn(mockServer);
        Mockito.when(mockCtx.getBean(PropManager.class)).thenReturn(mockPropManager);
        Mockito.when(mockCtx.getBean(EAIServiceMonitor.class)).thenReturn(mockMonitor);

        Field ctxField = ApplicationContextProvider.class.getDeclaredField("context");
        ctxField.setAccessible(true);
        ctxField.set(null, mockCtx);

        System.setProperty(StandardMessageManager.STANDARD_MESSAGE_CONFIG,
                "src/test/resources/standard-message-config-test.properties");
        manager = StandardMessageManager.getInstance();
        manager.init();
    }

    @BeforeEach
    void setUp() {
        mapper      = manager.getMapper();
        flatReader  = manager.getReader("FLAT");
        jsonReader  = (JsonReader) manager.getReader("JSON");
        coordinator = new StandardMessageCoordinatorDJB();
    }

    // ================================================================
    // 1. FlatReader 등록 확인
    // ================================================================

    @Test
    void FlatReader_등록_확인() {
        assertNotNull(flatReader, "FlatReader가 등록되지 않음 (reader.FLAT 설정 확인)");
    }

    // ================================================================
    // 2. 비ERP 요청 FLAT 왕복 — HEAD 보존, EZDATA/MSG 비포함
    //
    // [버그] 수정 전: EZDATA(1500B) + MSG(10B) 가 FLAT에 잘못 포함
    //         → FlatReader 파싱 시 overflow 예외 발생
    // [수정] StandardItem.toByteArray() GROUP 케이스: size==0 이면 무조건 skip
    // ================================================================

    @Test
    void 비ERP요청_FLAT_파싱_예외없음() throws Exception {
        StandardMessage src = parseJson(NON_ERP_REQ_JSON);
        String flatStr = src.toFixedString(false, FLAT_CHARSET);

        StandardMessage dst = manager.getStandardMessage();
        assertDoesNotThrow(() -> flatReader.parse(dst, flatStr),
                "비ERP FLAT 파싱 중 예외 발생 — EZDATA 바이트 오버플로우 버그 확인 필요\n"
                + "원인: StandardItem.toByteArray() GROUP size==0 skip 조건 미적용");
    }

    @Test
    void 비ERP요청_FLAT_왕복_HEAD_guid_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(NON_ERP_REQ_JSON));

        assertEquals(TEST_GUID, dst.findItemValue("HEAD.guid").trim(),
                "FLAT 왕복 후 HEAD.guid 불일치");
    }

    @Test
    void 비ERP요청_FLAT_왕복_HEAD_if_id_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(NON_ERP_REQ_JSON));

        assertEquals(TEST_IF_ID, dst.findItemValue("HEAD.if_id").trim(),
                "FLAT 왕복 후 HEAD.if_id 불일치");
    }

    @Test
    void 비ERP요청_FLAT_왕복_HEAD_dman_rspn_dvcd_S() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(NON_ERP_REQ_JSON));

        assertEquals("S", dst.findItemValue("HEAD.dman_rspn_dvcd").trim(),
                "FLAT 왕복 후 HEAD.dman_rspn_dvcd 불일치");
    }

    @Test
    void 비ERP요청_FLAT_왕복_HEAD_stnd_mesg_ver_R10() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(NON_ERP_REQ_JSON));

        assertEquals("R10", dst.findItemValue("HEAD.stnd_mesg_ver").trim(),
                "FLAT 왕복 후 HEAD.stnd_mesg_ver 불일치");
    }

    @Test
    void 비ERP요청_FLAT_왕복_DATA_data_dvcd_IO() throws Exception {
        // [버그] 수정 전: MSG 바이트가 잘못 포함 → DATA.data_dvcd 자리에 MSG 바이트 읽힘 ("NM")
        StandardMessage dst = flatRoundTrip(parseJson(NON_ERP_REQ_JSON));

        assertEquals("IO", dst.findItemValue("DATA.data_dvcd").trim(),
                "FLAT 왕복 후 DATA.data_dvcd 불일치 — MSG 바이트 오염 가능성");
    }

    @Test
    void 비ERP요청_FLAT_왕복_EZDATA_비활성() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(NON_ERP_REQ_JSON));

        StandardItem ezdataItem = dst.findItem("EZDATA");
        assertTrue(ezdataItem == null || ezdataItem.isHidden() || ezdataItem.getSize() == 0,
                "비ERP FLAT 왕복 후 EZDATA 블록이 활성 상태이면 안 됨");
    }

    // ================================================================
    // 3. ERP 요청 FLAT 왕복 — EZDATA 포함
    // ================================================================

    @Test
    void ERP요청_FLAT_파싱_예외없음() throws Exception {
        StandardMessage src = parseJson(ERP_REQ_JSON);
        String flatStr = src.toFixedString(false, FLAT_CHARSET);

        StandardMessage dst = manager.getStandardMessage();
        assertDoesNotThrow(() -> flatReader.parse(dst, flatStr),
                "ERP FLAT 파싱 중 예외 발생");
    }

    @Test
    void ERP요청_FLAT_왕복_HEAD_guid_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals(TEST_GUID, dst.findItemValue("HEAD.guid").trim(),
                "ERP FLAT 왕복 후 HEAD.guid 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_HEAD_corp_tlwn_virt_brcd_ERP() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals("ERP", dst.findItemValue("HEAD.corp_tlwn_virt_brcd").trim(),
                "ERP FLAT 왕복 후 HEAD.corp_tlwn_virt_brcd 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_활성() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        StandardItem ezdataItem = dst.findItem("EZDATA");
        assertNotNull(ezdataItem, "ERP FLAT 왕복 후 EZDATA 블록 없음");
        assertFalse(ezdataItem.isHidden(), "ERP FLAT 왕복 후 EZDATA 블록이 hidden 상태");
        assertTrue(ezdataItem.getSize() > 0, "ERP FLAT 왕복 후 EZDATA 블록 size가 0");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_login_mhod_dvcd_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals("1", dst.findItemValue("EZDATA.login_mhod_dvcd").trim(),
                "ERP FLAT 왕복 후 EZDATA.login_mhod_dvcd 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_elfn_bnkn_dvcd_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals("01", dst.findItemValue("EZDATA.elfn_bnkn_dvcd").trim(),
                "ERP FLAT 왕복 후 EZDATA.elfn_bnkn_dvcd 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_usr_no_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals("USR0000000000001", dst.findItemValue("EZDATA.usr_no").trim(),
                "ERP FLAT 왕복 후 EZDATA.usr_no 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_rgsr_id_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals("REG_ID_000000001", dst.findItemValue("EZDATA.rgsr_id").trim(),
                "ERP FLAT 왕복 후 EZDATA.rgsr_id 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_secu_media_dvcd_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals("00", dst.findItemValue("EZDATA.secu_media_dvcd").trim(),
                "ERP FLAT 왕복 후 EZDATA.secu_media_dvcd 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_dup_tx_yn_보존() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals("N", dst.findItemValue("EZDATA.dup_tx_yn").trim(),
                "ERP FLAT 왕복 후 EZDATA.dup_tx_yn 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_NUMBER_page_no_보존() throws Exception {
        // page_no 는 NUMBER 타입: "00001" → toTypeValue() 선행 0 제거 → "1"
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        String pageNo = dst.findItemValue("EZDATA.page_no").trim();
        assertFalse(pageNo.isEmpty(), "ERP FLAT 왕복 후 EZDATA.page_no가 비어 있음");
        assertEquals(1, Integer.parseInt(pageNo),
                "ERP FLAT 왕복 후 EZDATA.page_no 값 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_EZDATA_NUMBER_page_size_보존() throws Exception {
        // page_size 는 NUMBER 타입: "00020" → "20"
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        String pageSize = dst.findItemValue("EZDATA.page_size").trim();
        assertFalse(pageSize.isEmpty(), "ERP FLAT 왕복 후 EZDATA.page_size가 비어 있음");
        assertEquals(20, Integer.parseInt(pageSize),
                "ERP FLAT 왕복 후 EZDATA.page_size 값 불일치");
    }

    @Test
    void ERP요청_FLAT_왕복_DATA_data_dvcd_IO() throws Exception {
        // [버그] 수정 전: MSG 바이트가 잘못 포함 → DATA.data_dvcd 자리에 MSG 바이트 읽힘
        StandardMessage dst = flatRoundTrip(parseJson(ERP_REQ_JSON));

        assertEquals("IO", dst.findItemValue("DATA.data_dvcd").trim(),
                "ERP FLAT 왕복 후 DATA.data_dvcd 불일치 — MSG 바이트 오염 가능성");
    }

    // ================================================================
    // 4. 오류 응답 FLAT 왕복 — MSG EM 포함
    //    coordinateSetStandardMessageError() → MSG.size=1, MAIN_MSG.size=1
    // ================================================================

    @Test
    void 오류응답_FLAT_파싱_예외없음() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateSetStandardMessageError(src, mapper, "RECEAICMM999", "테스트 오류");
        String flatStr = src.toFixedString(false, FLAT_CHARSET);

        StandardMessage dst = manager.getStandardMessage();
        assertDoesNotThrow(() -> flatReader.parse(dst, flatStr),
                "오류응답 FLAT 파싱 중 예외 발생");
    }

    @Test
    void 오류응답_FLAT_왕복_HEAD_procs_rslt_dvcd_F() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateSetStandardMessageError(src, mapper, "RECEAICMM999", "테스트 오류");

        assertEquals("F", flatRoundTrip(src).findItemValue("HEAD.procs_rslt_dvcd").trim(),
                "오류응답 FLAT 왕복 후 HEAD.procs_rslt_dvcd가 F가 아님");
    }

    @Test
    void 오류응답_FLAT_왕복_MSG_msg_dvcd_EM() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateSetStandardMessageError(src, mapper, "RECEAICMM999", "테스트 오류");

        assertEquals("EM", flatRoundTrip(src).findItemValue("MSG.msg_dvcd").trim(),
                "오류응답 FLAT 왕복 후 MSG.msg_dvcd가 EM이 아님");
    }

    @Test
    void 오류응답_FLAT_왕복_MSG_outp_atrb_cd_1_팝업() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateSetStandardMessageError(src, mapper, "RECEAICMM999", "테스트 오류");

        assertEquals("1", flatRoundTrip(src).findItemValue("MSG.MAIN_MSG.outp_atrb_cd").trim(),
                "오류응답 FLAT 왕복 후 MSG.MAIN_MSG.outp_atrb_cd가 1(팝업)이 아님");
    }

    @Test
    void 오류응답_FLAT_왕복_MSG_MAIN_MSG_활성() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateSetStandardMessageError(src, mapper, "RECEAICMM999", "테스트 오류");

        StandardItem mainMsgItem = flatRoundTrip(src).findItem("MSG.MAIN_MSG");
        assertNotNull(mainMsgItem, "오류응답 FLAT 왕복 후 MSG.MAIN_MSG 블록 없음");
        assertTrue(mainMsgItem.getSize() > 0,
                "오류응답 FLAT 왕복 후 MSG.MAIN_MSG size가 0");
    }

    // ================================================================
    // 5. 정상 응답 FLAT 왕복 — MSG NM 포함
    //    coordinateAfterRecvNonStdSyncResponse() → MSG.size=1, MAIN_MSG.size=1
    // ================================================================

    @Test
    void 정상응답_FLAT_파싱_예외없음() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateAfterRecvNonStdSyncResponse(src);
        String flatStr = src.toFixedString(false, FLAT_CHARSET);

        StandardMessage dst = manager.getStandardMessage();
        assertDoesNotThrow(() -> flatReader.parse(dst, flatStr),
                "정상응답 FLAT 파싱 중 예외 발생");
    }

    @Test
    void 정상응답_FLAT_왕복_MSG_msg_dvcd_NM() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateAfterRecvNonStdSyncResponse(src);

        assertEquals("NM", flatRoundTrip(src).findItemValue("MSG.msg_dvcd").trim(),
                "정상응답 FLAT 왕복 후 MSG.msg_dvcd가 NM이 아님");
    }

    @Test
    void 정상응답_FLAT_왕복_MSG_활성() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateAfterRecvNonStdSyncResponse(src);
        StandardMessage dst = flatRoundTrip(src);

        StandardItem msgItem = dst.findItem("MSG");
        assertNotNull(msgItem, "정상응답 FLAT 왕복 후 MSG 블록 없음");
        assertFalse(msgItem.isHidden(), "정상응답 FLAT 왕복 후 MSG 블록이 hidden 상태");
        assertTrue(msgItem.getSize() > 0, "정상응답 FLAT 왕복 후 MSG 블록 size가 0");
    }

    @Test
    void 정상응답_FLAT_왕복_MSG_MAIN_MSG_활성() throws Exception {
        StandardMessage src = manager.getStandardMessage();
        coordinator.coordinateAfterRecvNonStdSyncResponse(src);

        StandardItem mainMsgItem = flatRoundTrip(src).findItem("MSG.MAIN_MSG");
        assertNotNull(mainMsgItem, "정상응답 FLAT 왕복 후 MSG.MAIN_MSG 블록 없음");
        assertTrue(mainMsgItem.getSize() > 0,
                "정상응답 FLAT 왕복 후 MSG.MAIN_MSG size가 0");
    }

    // ================================================================
    // 6. 거래로그 시나리오 — FLAT 바이트 길이로 조건부 블록 포함 여부 검증
    //
    // [버그] 수정 전: 비ERP 에도 EZDATA(1500B) 포함 → ERP 와 길이 동일
    // [수정] 비ERP FLAT < ERP FLAT (EZDATA 1500바이트 차이)
    // ================================================================

    @Test
    void 거래로그_비ERP_FLAT_길이가_ERP_FLAT_보다_짧음() throws Exception {
        byte[] nonErpBytes = parseJson(NON_ERP_REQ_JSON)
                .toFixedString(false, FLAT_CHARSET).getBytes(FLAT_CHARSET);
        byte[] erpBytes = parseJson(ERP_REQ_JSON)
                .toFixedString(false, FLAT_CHARSET).getBytes(FLAT_CHARSET);

        // EZDATA = 1500바이트 — ERP FLAT 이 비ERP FLAT 보다 길어야 함
        // 버그 발생 시: 둘 다 EZDATA 포함 → 길이 동일 → 검증 실패
        assertTrue(erpBytes.length > nonErpBytes.length,
                String.format("ERP FLAT 길이(%d) > 비ERP FLAT 길이(%d) 이어야 함 (EZDATA 1500B 차이)",
                        erpBytes.length, nonErpBytes.length));
    }

    @Test
    void 거래로그_요청_FLAT_길이가_응답_FLAT_보다_짧음() throws Exception {
        // 요청(dman_rspn_dvcd=S): MSG 비활성 / 응답: MSG 활성 → 응답이 더 긺
        StandardMessage rspMsg = manager.getStandardMessage();
        coordinator.coordinateAfterRecvNonStdSyncResponse(rspMsg);

        byte[] reqBytes = parseJson(NON_ERP_REQ_JSON)
                .toFixedString(false, FLAT_CHARSET).getBytes(FLAT_CHARSET);
        byte[] rspBytes = rspMsg.toFixedString(false, FLAT_CHARSET).getBytes(FLAT_CHARSET);

        // 버그 발생 시: 요청도 MSG 바이트 포함 → 길이 역전 가능
        assertTrue(rspBytes.length > reqBytes.length,
                String.format("응답 FLAT 길이(%d) > 요청 FLAT 길이(%d) 이어야 함 (MSG 블록 포함 여부)",
                        rspBytes.length, reqBytes.length));
    }
    
    @Test
    void 비ERP요청_FLAT_왕복_HEAD_chnl_tycd_EAI값확인() throws Exception {
        StandardMessage dst = flatRoundTrip(parseJson(NON_ERP_REQ_JSON));

        assertEquals("EAI", dst.findItemValue("HEAD.chnl_tycd").trim(),
                "FLAT 왕복 후 HEAD.chnl_tycd EAI 확인");
    }

    

    // ================================================================
    // helpers
    // ================================================================

    private StandardMessage parseJson(String json) throws Exception {
        StandardMessage msg = manager.getStandardMessage();
        jsonReader.parse(msg, json);
        return msg;
    }

    /**
     * FLAT 왕복: src → toFixedString(false, euc-kr) → FlatReader.parse() → dst
     * STDMessageDAO.addRestoreSTDMessage / getRestoreSTDMessage 와 동일한 흐름
     */
    private StandardMessage flatRoundTrip(StandardMessage src) throws Exception {
        String flatStr = src.toFixedString(false, FLAT_CHARSET);
        StandardMessage dst = manager.getStandardMessage();
        flatReader.parse(dst, flatStr);
        return dst;
    }
}
