package com.caliverse.admin.logs.logservice.businesslogservice;

import com.caliverse.admin.domain.entity.EInputType;
import com.caliverse.admin.domain.entity.Log;
import com.caliverse.admin.domain.entity.common.SearchUserType;
import com.caliverse.admin.domain.entity.log.GenericLog;
import com.caliverse.admin.domain.request.LogGenericRequest;
import com.caliverse.admin.global.common.constants.AdminConstants;
import com.caliverse.admin.global.common.utils.CommonUtils;
import com.caliverse.admin.logs.Indicatordomain.GenericMongoLog;
import com.caliverse.admin.logs.entity.LogAction;
import com.caliverse.admin.logs.entity.LogDomain;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
public class BusinessLogGenericService extends BusinessLogServiceBase {

    @Value("${spring.profiles.active}")
    private String activeProfile;


    public BusinessLogGenericService(@Qualifier("mongoBusinessLogTemplate") MongoTemplate mongoTemplate) {
        super(mongoTemplate);
    }

    public <T> List<T> loadBusinessLogData(LogGenericRequest logGenericRequest, Class<T> class1){
        List<GenericMongoLog> rawLogs = loadRawLogData(logGenericRequest);

        List<T> processedLogs = rawLogs.parallelStream()
                .map(rawLog -> parseLog(rawLog, class1))
                .filter(logData -> {
                    if (logData == null) {
                        log.warn("Failed to parse log data - null result returned");
                        return false;
                    }
                    return true;
                })
                // 추후 조건에 따른 조회방식 처리 필요할듯(정규식은 느리다)
//                .filter(logData -> {
//                    LogAction action = logGenericRequest.getLogAction();
//                    if(action == null || action.equals(LogAction.None)) return true;
//                    GenericLog log = (GenericLog) logData;
//                    return action.name().equals(log.getAction());
//                })
//                .filter(logData -> {
//                    LogDomain domain = logGenericRequest.getLogDomain();
//                    if(domain == null || domain.equals(LogDomain.BASE)) return true;
//                    GenericLog log = (GenericLog) logData;
//                    return domain.name().equals(log.getDomain());
//                })
//                .filter(logData -> {
//                    SearchUserType searchType = logGenericRequest.getSearchType();
//                    String searchData = logGenericRequest.getSearchData();
//                    if(searchData == null || searchData.isEmpty()) return true;
//
//                    GenericLog log = (GenericLog) logData;
//
//                    switch (searchType) {
//                        case ACCOUNT:
//                            return searchData.equals(log.getAccountId());
//                        case GUID:
//                            return searchData.equals(log.getUserGuid());
//                        case NICKNAME:
//                            return searchData.equals(log.getUserNickname());
//                        default:
//                            return false;
//                    }
//                })
//                .filter(logData -> {
//                    String tranId = logGenericRequest.getTranId();
//                    if(tranId == null || tranId.isEmpty()) return true;
//                    GenericLog log = (GenericLog) logData;
//                    return tranId.equals(log.getTranId());
//                })
                .toList();

        return processedLogs;
    }

    // 정규식으로 조회시간이 오래걸린다
//    public <T> List<T> loadBusinessLogData(LogGenericRequest logGenericRequest, Class<T> class1) {
//
//        String startTime = logGenericRequest.getStartDt().toString().substring(0, 10);
//        String endTime = logGenericRequest.getEndDt().toString().substring(0, 10);
//        LogAction logAction = logGenericRequest.getLogAction();
//        LogDomain logDomain = logGenericRequest.getLogDomain();
//        SearchUserType searchUserType = logGenericRequest.getSearchType();
//        String searchData = logGenericRequest.getSearchData();
//        String tranId = logGenericRequest.getTranId();
//        List<LogGenericRequest.LogFilter> filters = logGenericRequest.getFilters();
//
//        Criteria criteria = makeCriteria(startTime, endTime);
//        if(logAction != null && !logAction.equals(LogAction.None)) {
//            criteria.and(AdminConstants.MONGO_DB_KEY_MESSAGE)
//                    .regex(String.format(AdminConstants.REGEX_MSG_ACTION, logAction.name()));
//        }
//
//        List<AggregationOperation> operations = setDefaultOperation(criteria);
//
//
//        if(logDomain != null && !logDomain.equals(LogDomain.BASE)) {
//            operations.add(context ->
//                    new Document("$match",
//                            new Document(AdminConstants.MONGO_DB_KEY_DOMAIN, logDomain.name())
//                    )
//            );
//        }
//
//        if(searchUserType != null && !searchData.isEmpty() && !searchUserType.equals(SearchUserType.NONE)) {
//            switch(searchUserType){
//                case ACCOUNT:
//                    operations.add(context ->
//                            new Document("$match",
//                                    new Document(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, searchData)
//                            )
//                    );
//                    break;
//                case GUID:
//                    operations.add(context ->
//                            new Document("$match",
//                                    new Document(AdminConstants.MONGO_DB_KEY_USER_GUID, searchData)
//                            )
//                    );
//                    break;
//                case NICKNAME:
//                    operations.add(context ->
//                            new Document("$match",
//                                    new Document(AdminConstants.MONGO_DB_KEY_USER_NICKNAME, searchData)
//                            )
//                    );
//                    break;
//            }
//        }
//
//        if(tranId != null && !tranId.isEmpty()) {
//            operations.add(context ->
//                    new Document("$match",
//                            new Document(AdminConstants.MONGO_DB_KEY_TRAN_ID, tranId)
//                    )
//            );
//        }
//
//        if(filters != null && !filters.isEmpty()) {
//            List<Document> filterConditions = new ArrayList<>();
//
//            for (LogGenericRequest.LogFilter filter : filters) {
//                if (filter.getFieldName() != null && !filter.getFieldName().isEmpty() &&
//                        filter.getValue() != null) {
//
//                    Object convertedValue = null;
//                    String valueStr = filter.getValue();
//
//                    // OR 조건 패턴 확인 (괄호와 파이프 문자 포함)
//                    boolean isOrPattern = valueStr.startsWith("(") && valueStr.endsWith(")") && valueStr.contains("|");
//
//                    if (isOrPattern) {
//                        // OR 패턴은 문자열 그대로 사용
//                        convertedValue = valueStr;
//                    } else {
//                        // 일반 값 변환
//                        switch (filter.getFieldType()) {
//                            case Number:
//                                try {
//                                    if (valueStr.contains(".")) {
//                                        convertedValue = Double.parseDouble(valueStr);
//                                    } else {
//                                        convertedValue = Long.parseLong(valueStr);
//                                    }
//                                } catch (NumberFormatException e) {
//                                    log.warn("Failed to convert value to number: {}", valueStr, e);
//                                    convertedValue = valueStr;
//                                }
//                                break;
//
//                            case Boolean:
//                                convertedValue = Boolean.parseBoolean(valueStr);
//                                break;
//
//                            case String:
//                            case None:
//                            default:
//                                convertedValue = valueStr;
//                                break;
//                        }
//                    }
//
//                    // 중첩된 JSON 구조에 맞게 $regex를 사용한 패턴 매칭
//                    String regexPattern;
//
//                    if (isOrPattern) {
//                        // OR 패턴 처리
//                        String innerValues = valueStr.substring(1, valueStr.length() - 1);
//                        String[] parts = innerValues.split("\\|");
//
//                        // 필드 이름과 함께 각 값에 대한 정규식 패턴 구성
//                        StringBuilder orPatternBuilder = new StringBuilder();
//                        orPatternBuilder.append("\"").append(filter.getFieldName()).append("\":(");
//
//                        for (int i = 0; i < parts.length; i++) {
//                            if (i > 0) orPatternBuilder.append("|");
//
//                            String part = parts[i].trim();
//                            // 필드 타입에 맞게 값 형식화
//                            if (filter.getFieldType() == EInputType.String) {
//                                orPatternBuilder.append("\"").append(part.replace("\"", "\\\"")).append("\"");
//                            } else {
//                                orPatternBuilder.append(part);
//                            }
//                        }
//
//                        orPatternBuilder.append(")");
//                        regexPattern = orPatternBuilder.toString();
//                    } else {
//                        // 단일 값 처리
//                        regexPattern = "\"" + filter.getFieldName() + "\":" + CommonUtils.formatValueForRegex(convertedValue);
//                    }
//
//                    Document filterDoc = new Document("message", new Document("$regex", regexPattern));
//                    filterConditions.add(filterDoc);
//                }
//            }
//
//            if (!filterConditions.isEmpty()) {
//                operations.add(context -> new Document("$match", new Document("$and", filterConditions)));
//            }
//        }
//
//        // 최종 출력 형식
//        operations.add(context ->
//                new Document("$sort",
//                        new Document("logTime", logGenericRequest.getOrderBy().equals("ASC") ? 1 : -1)
//                                .append(AdminConstants.MONGO_DB_KEY_TRAN_ID, 1)
//                )
//        );
//
//        if (logGenericRequest.getPageNo() != null && logGenericRequest.getPageSize() != null) {
//            int skip = (logGenericRequest.getPageNo() - 1) * logGenericRequest.getPageSize();
//            operations.add(context -> new Document("$skip", skip));
//            operations.add(context -> new Document("$limit", logGenericRequest.getPageSize()));
//        }
//
//        AggregationOptions options = AggregationOptions.builder()
//                .allowDiskUse(true)
//                .maxTime(Duration.ofSeconds(600))
//                .build();
//        Aggregation aggregation = Aggregation.newAggregation(operations).withOptions(options);
//        log.info("loadBusinessLogData Generic Query: {}", aggregation);
//        AggregationResults<T> results = getMongoTemplate()
//                .aggregate(aggregation, AdminConstants.MONGO_DB_COLLECTION_LOG, class1);
//        return results.getMappedResults();
//    }

    protected Criteria makeCriteria(String startTime, String endTime) {
        return new Criteria()
                .andOperator(
                        Criteria.where(AdminConstants.MONGO_DB_KEY_LOGTIME).gte(startTime).lt(endTime)
                );
    }

    private List<GenericMongoLog> loadRawLogData(LogGenericRequest logGenericRequest) {
        String startTime = logGenericRequest.getStartDt().toString().substring(0, 10);
        String endTime = logGenericRequest.getEndDt().toString().substring(0, 10);
        LogAction logAction = logGenericRequest.getLogAction();
        LogDomain logDomain = logGenericRequest.getLogDomain();
        SearchUserType searchUserType = logGenericRequest.getSearchType();
        String searchData = logGenericRequest.getSearchData();
        String tranId = logGenericRequest.getTranId();
        List<LogGenericRequest.LogFilter> filters = logGenericRequest.getFilters();

        Criteria criteria = makeCriteria(startTime, endTime);

        List<AggregationOperation> operations = new ArrayList<>();

        operations.add(Aggregation.match(criteria));

        if(logDomain != null && !logDomain.equals(LogDomain.BASE)) {
            operations.add(context ->
                    new Document("$match",
                            new Document(AdminConstants.MONGO_DB_KEY_MESSAGE,
                                    new Document("$regex", String.format(AdminConstants.REGEX_MSG_DOMAIN, logDomain.name()))
                            )
                    )
            );
        }

        if(logAction != null && !logAction.equals(LogAction.None)) {
            operations.add(context ->
                    new Document("$match",
                            new Document(AdminConstants.MONGO_DB_KEY_MESSAGE,
                                    new Document("$regex", String.format(AdminConstants.REGEX_MSG_ACTION, logAction.name()))
                            )
                    )
            );
        }

        if(searchUserType != null && !searchData.isEmpty() && !searchUserType.equals(SearchUserType.NONE)) {
            switch(searchUserType){
                case ACCOUNT:
                    operations.add(context ->
                            new Document("$match",
                                    new Document(AdminConstants.MONGO_DB_KEY_MESSAGE,
                                            new Document("$regex", String.format("\"AccountId\":\"%s\"", searchData))
                                    )
                            )
                    );
                    break;
                case GUID:
                    operations.add(context ->
                            new Document("$match",
                                    new Document(AdminConstants.MONGO_DB_KEY_MESSAGE,
                                            new Document("$regex", String.format("\"UserGuid\":\"%s\"", searchData))
                                    )
                            )
                    );
                    break;
                case NICKNAME:
                    operations.add(context ->
                            new Document("$match",
                                    new Document(AdminConstants.MONGO_DB_KEY_MESSAGE,
                                            new Document("$regex", String.format("\"UserNickname\":\"%s\"", searchData))
                                    )
                            )
                    );
                    break;
            }
        }

        if(tranId != null && !tranId.isEmpty()) {
            operations.add(context ->
                    new Document("$match",
                            new Document(AdminConstants.MONGO_DB_KEY_MESSAGE,
                                new Document("$regex", String.format("\"TranId\":\"%s\"", tranId))
                            )
                    )
            );
        }

        operations.add(context ->
                new Document("$project",
                        new Document(AdminConstants.MONGO_DB_KEY_LOGTIME, 1)
                                .append(AdminConstants.MONGO_DB_KEY_MESSAGE, 1)
                )
        );

        if(filters != null && !filters.isEmpty()) {
            List<Document> filterConditions = new ArrayList<>();

            for (LogGenericRequest.LogFilter filter : filters) {
                if (filter.getFieldName() != null && !filter.getFieldName().isEmpty() &&
                        filter.getValue() != null) {

                    Object convertedValue = null;
                    String valueStr = filter.getValue();

                    // OR 조건 패턴 확인 (괄호와 파이프 문자 포함)
                    boolean isOrPattern = valueStr.startsWith("(") && valueStr.endsWith(")") && valueStr.contains("|");

                    if (isOrPattern) {
                        // OR 패턴은 문자열 그대로 사용
                        convertedValue = valueStr;
                    } else {
                        // 일반 값 변환
                        switch (filter.getFieldType()) {
                            case Number:
                                try {
                                    if (valueStr.contains(".")) {
                                        convertedValue = Double.parseDouble(valueStr);
                                    } else {
                                        convertedValue = Long.parseLong(valueStr);
                                    }
                                } catch (NumberFormatException e) {
                                    log.warn("Failed to convert value to number: {}", valueStr, e);
                                    convertedValue = valueStr;
                                }
                                break;

                            case Boolean:
                                convertedValue = Boolean.parseBoolean(valueStr);
                                break;

                            case String:
                            case None:
                            default:
                                convertedValue = valueStr;
                                break;
                        }
                    }

                    // 중첩된 JSON 구조에 맞게 $regex를 사용한 패턴 매칭
                    String regexPattern;

                    if (isOrPattern) {
                        // OR 패턴 처리
                        String innerValues = valueStr.substring(1, valueStr.length() - 1);
                        String[] parts = innerValues.split("\\|");

                        // 필드 이름과 함께 각 값에 대한 정규식 패턴 구성
                        StringBuilder orPatternBuilder = new StringBuilder();
                        orPatternBuilder.append("\"").append(filter.getFieldName()).append("\":(");

                        for (int i = 0; i < parts.length; i++) {
                            if (i > 0) orPatternBuilder.append("|");

                            String part = parts[i].trim();
                            // 필드 타입에 맞게 값 형식화
                            if (filter.getFieldType() == EInputType.String) {
                                orPatternBuilder.append("\"").append(part.replace("\"", "\\\"")).append("\"");
                            } else {
                                orPatternBuilder.append(part);
                            }
                        }

                        orPatternBuilder.append(")");
                        regexPattern = orPatternBuilder.toString();
                    } else {
                        // 단일 값 처리
                        regexPattern = "\"" + filter.getFieldName() + "\":" + CommonUtils.formatValueForRegex(convertedValue);
                    }

                    Document filterDoc = new Document("message", new Document("$regex", regexPattern));
                    filterConditions.add(filterDoc);
                }
            }

            if (!filterConditions.isEmpty()) {
                operations.add(context -> new Document("$match", new Document("$and", filterConditions)));
            }
        }

        operations.add(context ->
                new Document("$sort",
                        new Document("logTime", logGenericRequest.getOrderBy().equals("ASC") ? 1 : -1)
                )
        );

        if (logGenericRequest.getPageNo() != null && logGenericRequest.getPageSize() != null) {
            int skip = (logGenericRequest.getPageNo() - 1) * logGenericRequest.getPageSize();
            operations.add(context -> new Document("$skip", skip));
            operations.add(context -> new Document("$limit", logGenericRequest.getPageSize()));
        }

        AggregationOptions options = AggregationOptions.builder()
                .allowDiskUse(true)
                .maxTime(Duration.ofSeconds(600))
                .build();

        Aggregation aggregation = Aggregation.newAggregation(operations).withOptions(options);
        log.info("loadRawLogData Generic Query: {}", aggregation);

        AggregationResults<GenericMongoLog> results = getMongoTemplate()
                .aggregate(aggregation, AdminConstants.MONGO_DB_COLLECTION_LOG, GenericMongoLog.class);

        return results.getMappedResults();
    }

    public Integer getRawLogCount(LogGenericRequest logGenericRequest) {
        String startTime = logGenericRequest.getStartDt().toString().substring(0, 10);
        String endTime = logGenericRequest.getEndDt().toString().substring(0, 10);
        LogAction logAction = logGenericRequest.getLogAction();
        LogDomain logDomain = logGenericRequest.getLogDomain();
        SearchUserType searchUserType = logGenericRequest.getSearchType();
        String searchData = logGenericRequest.getSearchData();
        String tranId = logGenericRequest.getTranId();
        List<LogGenericRequest.LogFilter> filters = logGenericRequest.getFilters();

        Criteria criteria = makeCriteria(startTime, endTime);

        List<AggregationOperation> operations = new ArrayList<>();

        operations.add(Aggregation.match(criteria));

        operations.add(context ->
                new Document("$addFields",
                        new Document("userGuid",
                                new Document("$let",
                                        new Document("vars",
                                                new Document("found",
                                                        new Document("$regexFind",
                                                                new Document("input", "$message")
                                                                        .append("regex", "\"UserGuid\":\"([^\"]+)\"")
                                                        )
                                                )
                                        )
                                                .append("in",
                                                        new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                )
                                )
                        )
                                .append("userNickname",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("found",
                                                                new Document("$regexFind",
                                                                        new Document("input", "$message")
                                                                                .append("regex", "\"UserNickname\":\"([^\"]+)\"")
                                                                )
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                        )
                                        )
                                )
                                .append("accountId",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("found",
                                                                new Document("$regexFind",
                                                                        new Document("input", "$message")
                                                                                .append("regex", "\"AccountId\":\"([^\"]+)\"")
                                                                )
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                        )
                                        )
                                )
                                .append("tranId",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("found",
                                                                new Document("$regexFind",
                                                                        new Document("input", "$message")
                                                                                .append("regex", "\"TranId\":\"([^\"]+)\"")
                                                                )
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                        )
                                        )
                                )
                                .append("action",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("found",
                                                                new Document("$regexFind",
                                                                        new Document("input", "$message")
                                                                                .append("regex", "\"Action\":\"([^\"]+)\"")
                                                                )
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                        )
                                        )
                                )
                                .append("domain",
                                        new Document("$let",
                                                new Document("vars",
                                                        new Document("found",
                                                                new Document("$regexFind",
                                                                        new Document("input", "$message")
                                                                                .append("regex", "\"Domain\":\"([^\"]+)\"")
                                                                )
                                                        )
                                                )
                                                        .append("in",
                                                                new Document("$arrayElemAt", Arrays.asList("$$found.captures", 0))
                                                        )
                                        )
                                )
                )
        );

        if(logAction != null && !logAction.equals(LogAction.None)) {
            operations.add(context ->
                    new Document("$match",
                            new Document(AdminConstants.MONGO_DB_KEY_ACTION, logAction.name())
                    )
            );
        }

        if(logDomain != null && !logDomain.equals(LogDomain.BASE)) {
            operations.add(context ->
                    new Document("$match",
                            new Document(AdminConstants.MONGO_DB_KEY_DOMAIN, logDomain.name())
                    )
            );
        }

        if(searchUserType != null && !searchData.isEmpty() && !searchUserType.equals(SearchUserType.NONE)) {
            switch(searchUserType){
                case ACCOUNT:
                    operations.add(context ->
                            new Document("$match",
                                    new Document(AdminConstants.MONGO_DB_KEY_ACCOUNT_ID, searchData)
                            )
                    );
                    break;
                case GUID:
                    operations.add(context ->
                            new Document("$match",
                                    new Document(AdminConstants.MONGO_DB_KEY_USER_GUID, searchData)
                            )
                    );
                    break;
                case NICKNAME:
                    operations.add(context ->
                            new Document("$match",
                                    new Document(AdminConstants.MONGO_DB_KEY_USER_NICKNAME, searchData)
                            )
                    );
                    break;
            }
        }

        if(tranId != null && !tranId.isEmpty()) {
            operations.add(context ->
                    new Document("$match",
                            new Document(AdminConstants.MONGO_DB_KEY_TRAN_ID, tranId)
                    )
            );
        }

        if(filters != null && !filters.isEmpty()) {
            List<Document> filterConditions = new ArrayList<>();

            for (LogGenericRequest.LogFilter filter : filters) {
                if (filter.getFieldName() != null && !filter.getFieldName().isEmpty() &&
                        filter.getValue() != null) {

                    Object convertedValue = null;
                    String valueStr = filter.getValue();

                    // OR 조건 패턴 확인 (괄호와 파이프 문자 포함)
                    boolean isOrPattern = valueStr.startsWith("(") && valueStr.endsWith(")") && valueStr.contains("|");

                    if (isOrPattern) {
                        // OR 패턴은 문자열 그대로 사용
                        convertedValue = valueStr;
                    } else {
                        // 일반 값 변환
                        switch (filter.getFieldType()) {
                            case Number:
                                try {
                                    if (valueStr.contains(".")) {
                                        convertedValue = Double.parseDouble(valueStr);
                                    } else {
                                        convertedValue = Long.parseLong(valueStr);
                                    }
                                } catch (NumberFormatException e) {
                                    log.warn("Failed to convert value to number: {}", valueStr, e);
                                    convertedValue = valueStr;
                                }
                                break;

                            case Boolean:
                                convertedValue = Boolean.parseBoolean(valueStr);
                                break;

                            case String:
                            case None:
                            default:
                                convertedValue = valueStr;
                                break;
                        }
                    }

                    // 중첩된 JSON 구조에 맞게 $regex를 사용한 패턴 매칭
                    String regexPattern;

                    if (isOrPattern) {
                        // OR 패턴 처리
                        String innerValues = valueStr.substring(1, valueStr.length() - 1);
                        String[] parts = innerValues.split("\\|");

                        // 필드 이름과 함께 각 값에 대한 정규식 패턴 구성
                        StringBuilder orPatternBuilder = new StringBuilder();
                        orPatternBuilder.append("\"").append(filter.getFieldName()).append("\":(");

                        for (int i = 0; i < parts.length; i++) {
                            if (i > 0) orPatternBuilder.append("|");

                            String part = parts[i].trim();
                            // 필드 타입에 맞게 값 형식화
                            if (filter.getFieldType() == EInputType.String) {
                                orPatternBuilder.append("\"").append(part.replace("\"", "\\\"")).append("\"");
                            } else {
                                orPatternBuilder.append(part);
                            }
                        }

                        orPatternBuilder.append(")");
                        regexPattern = orPatternBuilder.toString();
                    } else {
                        // 단일 값 처리
                        regexPattern = "\"" + filter.getFieldName() + "\":" + CommonUtils.formatValueForRegex(convertedValue);
                    }

                    Document filterDoc = new Document("message", new Document("$regex", regexPattern));
                    filterConditions.add(filterDoc);
                }
            }

            if (!filterConditions.isEmpty()) {
                operations.add(context -> new Document("$match", new Document("$and", filterConditions)));
            }
        }

        operations.add(context ->
                new Document("$count", "totalCount")
        );

        AggregationOptions options = AggregationOptions.builder()
                .allowDiskUse(true)
                .maxTime(Duration.ofSeconds(600))
                .build();

        Aggregation aggregation = Aggregation.newAggregation(operations).withOptions(options);
        log.info("loadRawLogData Generic Query: {}", aggregation);

        AggregationResults<GenericMongoLog> results = getMongoTemplate()
                .aggregate(aggregation, AdminConstants.MONGO_DB_COLLECTION_LOG, GenericMongoLog.class);

        if (results.getMappedResults().isEmpty()) return 0;
        return results.getMappedResults().get(0).getTotalCount();
    }

    private <T> T parseLog(GenericMongoLog rawLog, Class<T> class1) {
        try {
            JsonNode messageNode = CommonUtils.stringByJsonNode(rawLog.getMessage());
            JsonNode header = messageNode.get("Header");
            JsonNode body = messageNode.get("Body");

            // 필드 추출 (정규식 대신 JSON 파싱)
            String userGuid = extractStringValue(header, "UserGuid");
            String userNickname = extractStringValue(header, "UserNickname");
            String accountId = extractStringValue(header, "AccountId");
            String tranId = extractStringValue(header, "TranId");
            String action = extractStringValue(body, "Action");
            String domain = extractDomainFromInfos(body);

            // 타겟 객체 생성
            if (class1 == GenericLog.class) {
                Map<String, Object> headerMap = CommonUtils.nodeByValue(header, Map.class);
                Map<String, Object> bodyMap = CommonUtils.nodeByValue(body, Map.class);

                GenericLog log = GenericLog.builder()
                        .logTime(rawLog.getLogTime())
                        .userGuid(userGuid)
                        .userNickname(userNickname)
                        .accountId(accountId)
                        .tranId(tranId)
                        .action(action)
                        .domain(domain)
                        .header(headerMap)
                        .body(bodyMap)
                        .build();

                return (T) log;
            }

        } catch (Exception e) {
            log.warn("Failed to parse log: {}", e.getMessage());
        }
        return null;
    }

    private String extractStringValue(JsonNode node, String fieldName) {
        if (node == null) return "";

        JsonNode valueNode = node.get(fieldName);
        if(valueNode != null && !valueNode.isNull()) return valueNode.asText();

        if(node.has("Actor")){
            JsonNode actor = node.get("Actor");
            JsonNode actorNode = actor.get(fieldName);
            if(actorNode != null) {
                return actorNode.isNull() ? "" : actorNode.asText();
            }
        }

        return "";

    }

    private String extractDomainFromInfos(JsonNode body) {
        if (body != null && body.has("Infos") && body.get("Infos").isArray()) {
            JsonNode infos = body.get("Infos");
            if (!infos.isEmpty()) {
                JsonNode firstInfo = infos.get(0);
                if (firstInfo.has("Domain")) {
                    return firstInfo.get("Domain").asText();
                }
            }
        }
        return "";
    }
}
