package com.eactive.eai.transformer;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.nfunk.jep.JEP;

import com.eactive.eai.transformer.function.generic.ConcatN;

@DisplayName("ConcatN 가변 파라미터 함수 테스트")
class ConcatNFunctionTest {

    private JEP jep;

    @BeforeEach
    void setUp() {
        jep = new JEP();
        jep.addStandardFunctions();
        jep.addStandardConstants();
        jep.setAllowUndeclared(true);
        jep.addFunction("concatN", new ConcatN());
    }

    private String eval(String expr) throws Exception {
        jep.parseExpression(expr);
        assertFalse(jep.hasError(), "파싱 오류: " + jep.getErrorInfo());
        Object result = jep.getValueAsObject();
        assertFalse(jep.hasError(), "평가 오류: " + jep.getErrorInfo());
        return (String) result;
    }

    // ─────────────────────────────────────────────────────────────────
    // 파라미터 수 변화 테스트
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("파라미터 1개 — 입력값 그대로 반환")
    void testOneParam() throws Exception {
        jep.addVariable("a", "Hello");

        String result = eval("concatN(a)");

        assertEquals("Hello", result);
    }

    @Test
    @DisplayName("파라미터 2개 — 기존 Concat 동작과 동일")
    void testTwoParams() throws Exception {
        jep.addVariable("a", "Hello");
        jep.addVariable("b", "World");

        String result = eval("concatN(a, b)");

        assertEquals("HelloWorld", result);
    }

    @Test
    @DisplayName("파라미터 3개")
    void testThreeParams() throws Exception {
        jep.addVariable("a", "2024");
        jep.addVariable("sep", "-");
        jep.addVariable("b", "01");

        String result = eval("concatN(a, sep, b)");

        assertEquals("2024-01", result);
    }

    @Test
    @DisplayName("파라미터 5개 — 날짜 + 시간 조합")
    void testFiveParams() throws Exception {
        jep.addVariable("year",  "2024");
        jep.addVariable("dash",  "-");
        jep.addVariable("month", "05");
        jep.addVariable("dash2", "-");
        jep.addVariable("day",   "08");

        String result = eval("concatN(year, dash, month, dash2, day)");

        assertEquals("2024-05-08", result);
    }

    @Test
    @DisplayName("파라미터 7개 — 날짜 + 공백 + 시간 조합")
    void testSevenParams() throws Exception {
        jep.addVariable("year",   "2024");
        jep.addVariable("dash",   "-");
        jep.addVariable("month",  "05");
        jep.addVariable("day",    "08");
        jep.addVariable("space",  " ");
        jep.addVariable("hour",   "14");
        jep.addVariable("minute", "30");

        String result = eval("concatN(year, dash, month, dash, day, space, hour)");

        assertEquals("2024-05-08 14", result);
    }

    // ─────────────────────────────────────────────────────────────────
    // 변수 재할당 후 동일 표현식 재평가 — 파라미터 수 유지, 값만 변경
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("동일 표현식(3개), 변수 값만 변경하여 반복 평가")
    void testReuseExpressionWithDifferentValues() throws Exception {
        jep.parseExpression("concatN(code, dash, seq)");
        assertFalse(jep.hasError());

        jep.addVariable("code", "KOR");
        jep.addVariable("dash", "-");
        jep.addVariable("seq",  "001");
        assertEquals("KOR-001", (String) jep.getValueAsObject());

        jep.addVariable("code", "USA");
        jep.addVariable("seq",  "999");
        assertEquals("USA-999", (String) jep.getValueAsObject());

        jep.addVariable("code", "JPN");
        jep.addVariable("seq",  "042");
        assertEquals("JPN-042", (String) jep.getValueAsObject());
    }

    // ─────────────────────────────────────────────────────────────────
    // 리터럴 / 혼합 사용
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("리터럴 문자열만 5개 사용")
    void testLiteralsOnly() throws Exception {
        String result = eval("concatN(\"A\", \"B\", \"C\", \"D\", \"E\")");

        assertEquals("ABCDE", result);
    }

    @Test
    @DisplayName("변수와 리터럴 혼합 — 괄호로 감싸기")
    void testMixedVariableAndLiteral() throws Exception {
        jep.addVariable("code", "KOR");
        jep.addVariable("seq",  "001");

        String result = eval("concatN(\"[\", code, \"-\", seq, \"]\")");

        assertEquals("[KOR-001]", result);
    }

    // ─────────────────────────────────────────────────────────────────
    // 오류 케이스
    // ─────────────────────────────────────────────────────────────────

    @Test
    @DisplayName("비문자열(숫자) 파라미터 포함 → 오류 발생")
    void testNonStringParamCausesError() {
        // JEP 는 정수 리터럴을 Double 로 처리하므로 평가 시 오류가 발생해야 함
        jep.parseExpression("concatN(\"A\", 123, \"C\")");
        if (!jep.hasError()) {
            jep.getValueAsObject();
            assertTrue(jep.hasError(), "비문자열 파라미터가 포함된 경우 오류가 발생해야 합니다");
        }
    }
}
