package research.loghunter.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import research.loghunter.entity.Pattern;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;

@Service
@RequiredArgsConstructor
@Slf4j
public class LogParserService {

    // 로그 레벨 감지 패턴 (INFO, DEBUG, TRACE는 에러가 아님)
    private static final java.util.regex.Pattern LOG_LEVEL_PATTERN = java.util.regex.Pattern.compile(
            "\\b(INFO|DEBUG|TRACE|FINE|FINER|FINEST|CONFIG)\\b",
            java.util.regex.Pattern.CASE_INSENSITIVE
    );
    
    // 에러 레벨 패턴 (ERROR, WARN, FATAL 등)
    private static final java.util.regex.Pattern ERROR_LEVEL_PATTERN = java.util.regex.Pattern.compile(
            "\\b(ERROR|WARN|WARNING|FATAL|SEVERE|CRITICAL)\\b",
            java.util.regex.Pattern.CASE_INSENSITIVE
    );

    /**
     * 로그 내용에서 패턴 매칭
     */
    public List<MatchResult> parseAndMatch(String content, List<Pattern> patterns, String filePath) {
        List<MatchResult> results = new ArrayList<>();
        String[] lines = content.split("\n");

        for (Pattern pattern : patterns) {
            if (!pattern.getActive()) continue;
            
            try {
                java.util.regex.Pattern compiledPattern = java.util.regex.Pattern.compile(pattern.getRegex());
                
                // 제외 패턴 컴파일 (있는 경우)
                java.util.regex.Pattern excludePattern = null;
                if (pattern.getExcludeRegex() != null && !pattern.getExcludeRegex().isBlank()) {
                    try {
                        excludePattern = java.util.regex.Pattern.compile(pattern.getExcludeRegex());
                    } catch (Exception e) {
                        log.warn("Invalid exclude regex for pattern {}: {}", pattern.getName(), e.getMessage());
                    }
                }
                
                for (int i = 0; i < lines.length; i++) {
                    String line = lines[i];
                    Matcher matcher = compiledPattern.matcher(line);
                    
                    if (matcher.find()) {
                        // 1. 제외 패턴 체크
                        if (excludePattern != null && excludePattern.matcher(line).find()) {
                            log.debug("Excluded by excludeRegex: {}", line.substring(0, Math.min(100, line.length())));
                            continue;
                        }
                        
                        // 2. 로그 레벨 체크 - INFO/DEBUG/TRACE는 에러가 아님
                        if (isNonErrorLogLevel(line)) {
                            log.debug("Excluded by log level (INFO/DEBUG/TRACE): {}", 
                                    line.substring(0, Math.min(100, line.length())));
                            continue;
                        }
                        
                        // 컨텍스트 추출
                        String context = extractContext(lines, i, pattern.getContextLines());
                        String summary = createSummary(line);
                        
                        results.add(new MatchResult(
                                pattern,
                                filePath,
                                i + 1,  // 1-based line number
                                summary,
                                context,
                                pattern.getSeverity()
                        ));
                    }
                }
            } catch (Exception e) {
                log.warn("Failed to match pattern {}: {}", pattern.getName(), e.getMessage());
            }
        }

        return results;
    }

    /**
     * 로그 레벨이 INFO/DEBUG/TRACE인지 확인
     * - 에러 레벨(ERROR/WARN 등)이 있으면 false
     * - INFO/DEBUG/TRACE가 있으면 true (에러 아님)
     */
    private boolean isNonErrorLogLevel(String line) {
        // 에러 레벨이 있으면 에러임 (false 반환)
        if (ERROR_LEVEL_PATTERN.matcher(line).find()) {
            return false;
        }
        
        // INFO/DEBUG/TRACE가 있으면 에러 아님 (true 반환)
        if (LOG_LEVEL_PATTERN.matcher(line).find()) {
            return true;
        }
        
        // 레벨 정보가 없으면 패턴 매칭 결과를 사용 (에러로 간주)
        return false;
    }

    /**
     * 컨텍스트 추출 (에러 전후 라인)
     */
    private String extractContext(String[] lines, int matchIndex, int contextLines) {
        int start = Math.max(0, matchIndex - contextLines);
        int end = Math.min(lines.length - 1, matchIndex + contextLines);

        StringBuilder context = new StringBuilder();
        for (int i = start; i <= end; i++) {
            if (i == matchIndex) {
                context.append(">>> ");  // 에러 라인 표시
            }
            context.append(String.format("[%d] %s\n", i + 1, lines[i]));
        }
        return context.toString();
    }

    // 요약에서 제거할 날짜시간 패턴
    private static final java.util.regex.Pattern DATETIME_PREFIX_PATTERN = java.util.regex.Pattern.compile(
            "^\\s*" +
            "(" +
            "\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}[,.]?\\d{0,3}" +  // 2026-01-06 10:35:23,456
            "|\\d{2}-[A-Za-z]{3}-\\d{4} \\d{2}:\\d{2}:\\d{2}" +              // 06-Jan-2026 10:35:23
            "|[A-Za-z]{3} \\d{2},? \\d{4} \\d{2}:\\d{2}:\\d{2}" +            // Jan 06, 2026 10:35:23
            "|[A-Za-z]{3} \\d{2} \\d{2}:\\d{2}:\\d{2}" +                     // Jan 06 10:35:23
            "|\\[\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}[,.]?\\d{0,3}\\]" +  // [2026-01-06 10:35:23]
            ")" +
            "\\s*"
    );

    /**
     * 요약 생성 (첫 줄, 최대 200자, 앞부분 날짜시간 제거)
     */
    private String createSummary(String line) {
        if (line == null) return "";
        String trimmed = line.trim();
        
        // 앞부분 날짜시간 제거
        Matcher dtMatcher = DATETIME_PREFIX_PATTERN.matcher(trimmed);
        if (dtMatcher.find()) {
            trimmed = trimmed.substring(dtMatcher.end()).trim();
        }
        
        if (trimmed.length() <= 200) return trimmed;
        return trimmed.substring(0, 200) + "...";
    }

    // DTO
    public record MatchResult(
            Pattern pattern,
            String filePath,
            int lineNumber,
            String summary,
            String context,
            String severity
    ) {}
}
