package com.eactive.eai;

import com.eactive.eai.common.EAITable;
import com.eactive.eai.common.util.MessageUtil;
import org.json.simple.JSONValue;

/**
 * 테스트용 메시지 키 검증 클래스
 */
public class MessageKeyTest {


    /**
     * XML 또는 JSON 메시지의 성공 여부를 검증하는 테스트용 메소드
     * 추출된 값을 배열로 반환
     */
    public static TestResult isSuccessMessageTest(String messageType, Object message, String path, String expectedValue) {
        try {
            String extractedValue = "";

            if(EAITable.XML_TYPE.equals(messageType)) {
                if(message instanceof byte[]) {
                    extractedValue = extractXmlValue(new String((byte[])message), path);
                } else if(message instanceof String) {
                    extractedValue = extractXmlValue((String)message, path);
                }
            }
            else if(EAITable.JSON_TYPE.equals(messageType)) {
                Object json = JSONValue.parse((String)message);
                extractedValue = extractJsonValue(json, path);
            }

            if(extractedValue == null) extractedValue = "";
            boolean result = compareMessageValue(extractedValue, expectedValue);

            return new TestResult(result, extractedValue);

        } catch(Exception e) {
            e.printStackTrace();
            return new TestResult(false, "오류 발생: " + e.getMessage());
        }
    }

    // 테스트 결과를 담는 클래스
    static class TestResult {
        boolean success;
        String extractedValue;

        TestResult(boolean success, String extractedValue) {
            this.success = success;
            this.extractedValue = extractedValue;
        }
    }

    /**
     * 메시지 값 비교를 수행하는 메소드
     */
    private static boolean compareMessageValue(String actualValue, String expectedValue) {
        if(actualValue == null) actualValue = "";
        if(expectedValue == null) return false;

        // 예약어로 시작하는 경우
        if(expectedValue.startsWith("@")) {
            return compareWithReservedWord(actualValue, expectedValue);
        }

        // 일반 equals 비교
        return actualValue.equals(expectedValue);
    }

    /**
     * 예약어를 포함한 값 비교 처리
     */
    private static boolean compareWithReservedWord(String actualValue, String definition) {
        if (actualValue == null) actualValue = "";
        if (definition == null) return false;

        try {
            // 1. 기본 비교
            if (definition.startsWith("@eq:")) {
                return actualValue.equals(definition.substring(4));
            }
            if (definition.startsWith("@ne:")) {
                return !actualValue.equals(definition.substring(4));
            }
            if ("@null".equals(definition)) {
                return actualValue.isEmpty();
            }
            if ("@!null".equals(definition)) {
                return !actualValue.isEmpty();
            }

            // 2. 문자열 패턴
            if (definition.startsWith("@contains:")) {
                String[] values = definition.substring(10).split(",");
                for (String value : values) {
                    if (actualValue.equals(value.trim())) {
                        return true;
                    }
                }
                return false;
            }
            if (definition.startsWith("@!contains:")) {
                String[] values = definition.substring(11).split(",");
                for (String value : values) {
                    if (actualValue.equals(value.trim())) {
                        return false;
                    }
                }
                return true;
            }
            if (definition.startsWith("@starts:")) {
                return actualValue.startsWith(definition.substring(8));
            }
            if (definition.startsWith("@ends:")) {
                return actualValue.endsWith(definition.substring(6));
            }

            // 3. 숫자 비교
            if (definition.startsWith("@gt:") ||
                    definition.startsWith("@lt:") ||
                    definition.startsWith("@ge:") ||
                    definition.startsWith("@le:")) {

                double actualNum = Double.parseDouble(actualValue);
                if (definition.startsWith("@gt:")) {
                    double defNum = Double.parseDouble(definition.substring(4));
                    return actualNum > defNum;
                }
                if (definition.startsWith("@lt:")) {
                    double defNum = Double.parseDouble(definition.substring(4));
                    return actualNum < defNum;
                }
                if (definition.startsWith("@ge:")) {
                    double defNum = Double.parseDouble(definition.substring(4));
                    return actualNum >= defNum;
                }
                if (definition.startsWith("@le:")) {
                    double defNum = Double.parseDouble(definition.substring(4));
                    return actualNum <= defNum;
                }
            }

            return false;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * XML 값 추출 메소드
     */
    private static String extractXmlValue(String xmlMessage, String path) {
        if(path == null) return "";

        // XPath 형식인 경우
        if(path.startsWith("//") || path.startsWith("/")) {
            return MessageUtil.getXmlFldByXPath(xmlMessage, path);
        }

        // 단순 태그명인 경우
        return MessageUtil.getXmlFld(xmlMessage, path);
    }

    /**
     * JSON 값 추출 메소드
     */
    private static String extractJsonValue(Object json, String path) {
        if(path == null) return "";

        // JSONPath 형식인 경우
        if(path.startsWith("$.")) {
            return MessageUtil.getJSONValueByPath(json, path);
        }

        // 단순 키인 경우
        return MessageUtil.getJSONValue(json, path);
    }

    private static void testXml() {
        // System.out.println("\n=== XML 테스트 ===");

        // 단순 경로 테스트
        String xml1 = "<root><status>SUCCESS</status></root>";
        test("XML 단순 경로",
                EAITable.XML_TYPE,
                xml1,
                "status",
                "SUCCESS");

        // 중첩 경로 테스트 (일반 방식)
        String xml2 = "<root><response><header><resultCode>0000</resultCode></header></response></root>";
        test("XML 중첩 경로 (일반)",
                EAITable.XML_TYPE,
                xml2,
                "resultCode",
                "0000");

        // 중첩 경로 테스트 (XPath 방식)
        test("XML 중첩 경로 (XPath)",
                EAITable.XML_TYPE,
                xml2,
                "//response/header/resultCode",
                "0000");

        // 네임스페이스가 있는 XML
        String xml3 = "<ns:root xmlns:ns=\"http://test.com\"><ns:status>OK</ns:status></ns:root>";
        test("XML 네임스페이스",
                EAITable.XML_TYPE,
                xml3,
                "//ns:status",
                "OK");

        // 실패 케이스
        test("XML 실패 케이스",
                EAITable.XML_TYPE,
                xml1,
                "status",
                "FAIL");

        // 존재하지 않는 경로
        test("XML 없는 경로",
                EAITable.XML_TYPE,
                xml1,
                "nonexistent",
                "SUCCESS");
    }

    private static void testJson() {
        // System.out.println("\n=== JSON 테스트 ===");

        // 단순 경로 테스트
        String json1 = "{\"status\":\"success\"}";
        test("JSON 단순 경로",
                EAITable.JSON_TYPE,
                json1,
                "status",
                "success");

        // 중첩 경로 테스트 (일반 방식)
        String json2 = "{\"response\":{\"header\":{\"resultCode\":\"0000\"}}}";
        test("JSON 중첩 경로 (일반)",
                EAITable.JSON_TYPE,
                json2,
                "resultCode",
                "0000");

        // 중첩 경로 테스트 (JSONPath 방식)
        test("JSON 중첩 경로 (JSONPath)",
                EAITable.JSON_TYPE,
                json2,
                "$.response.header.resultCode",
                "0000");

        // 배열이 포함된 JSON
        String json3 = "{\"responses\":[{\"status\":\"success\"},{\"status\":\"pending\"}]}";
        test("JSON 배열 경로",
                EAITable.JSON_TYPE,
                json3,
                "$.responses[0].status",
                "success");

        // 실패 케이스
        test("JSON 실패 케이스",
                EAITable.JSON_TYPE,
                json1,
                "status",
                "failure");

        // 존재하지 않는 경로
        test("JSON 없는 경로",
                EAITable.JSON_TYPE,
                json1,
                "nonexistent",
                "success");
    }

    public static void main(String[] args) {
        testXml();
        testJson();
        testReservedWords();
    }

    private static void testReservedWords() {
        // System.out.println("\n=== 예약어 테스트 ===");

        // 1. 기본 비교 테스트
        test("예약어 - eq",
                EAITable.XML_TYPE,
                "<root><code>SUCCESS</code></root>",
                "code",
                "@eq:SUCCESS");

        test("예약어 - ne",
                EAITable.XML_TYPE,
                "<root><code>SUCCESS</code></root>",
                "code",
                "@ne:FAIL");

        test("예약어 - null",
                EAITable.XML_TYPE,
                "<root><code></code></root>",
                "code",
                "@null");

        test("예약어 - !null",
                EAITable.XML_TYPE,
                "<root><code>SUCCESS</code></root>",
                "code",
                "@!null");

        // 2. 문자열 패턴 테스트
        test("예약어 - contains",
                EAITable.JSON_TYPE,
                "{\"status\":\"success\"}",
                "status",
                "@contains:success,ok,done");

        test("예약어 - !contains",
                EAITable.JSON_TYPE,
                "{\"status\":\"success\"}",
                "status",
                "@!contains:fail,error,invalid");

        test("예약어 - starts",
                EAITable.JSON_TYPE,
                "{\"code\":\"SUCCESS_001\"}",
                "code",
                "@starts:SUCCESS");

        test("예약어 - ends",
                EAITable.JSON_TYPE,
                "{\"code\":\"ERR_001\"}",
                "code",
                "@ends:001");

        // 3. 숫자 비교 테스트
        test("예약어 - gt",
                EAITable.JSON_TYPE,
                "{\"value\":\"100\"}",
                "value",
                "@gt:50");

        test("예약어 - lt",
                EAITable.JSON_TYPE,
                "{\"value\":\"100\"}",
                "value",
                "@lt:150");

        test("예약어 - ge",
                EAITable.JSON_TYPE,
                "{\"value\":\"100\"}",
                "value",
                "@ge:100");

        test("예약어 - le",
                EAITable.JSON_TYPE,
                "{\"value\":\"100\"}",
                "value",
                "@le:100");

        // 4. 실패 케이스
        test("예약어 - 잘못된 숫자",
                EAITable.JSON_TYPE,
                "{\"value\":\"invalid\"}",
                "value",
                "@gt:50");

        test("예약어 - 잘못된 패턴",
                EAITable.JSON_TYPE,
                "{\"status\":\"success\"}",
                "status",
                "@invalid:pattern");
    }

    private static void test(String testName, String type, String message, String path, String expected) {
        TestResult result = isSuccessMessageTest(type, message, path, expected);
        // System.out.println(String.format( "테스트: %s\n결과: %s\n메시지: %s\n경로: %s\n기대값: %s\n추출된 값: [%s]\n",testName, result.success ? "성공" : "실패",message,path,expected,result.extractedValue ));
    }
}