package com.sensor.bridge;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 로깅 시스템 개선 클래스
 * 센서 데이터 처리 과정의 상세한 로깅 및 모니터링
 */
public class LoggingSystem {
    
    private static final Logger logger = LoggerFactory.getLogger(LoggingSystem.class);
    
    // 로깅 통계
    private final Map<String, AtomicLong> logCounters = new ConcurrentHashMap<>();
    private final Map<String, Long> lastLogTimes = new ConcurrentHashMap<>();
    
    // 로깅 설정
    private boolean enableDetailedLogging = true;
    private boolean enablePerformanceLogging = true;
    private boolean enableErrorLogging = true;
    
    // 로깅 임계값
    private static final int LOG_RATE_LIMIT = 100; // 초당 최대 로그 수
    private static final long LOG_TIME_WINDOW = 1000; // 1초 (밀리초)
    
    // 로그 타입 정의
    public enum LogType {
        TEMPERATURE_PARSING("온도 파싱"),
        SENSOR_DATA("센서 데이터"),
        PERFORMANCE("성능"),
        ERROR("에러"),
        MEMORY("메모리"),
        NETWORK("네트워크");
        
        private final String description;
        
        LogType(String description) {
            this.description = description;
        }
        
        public String getDescription() {
            return description;
        }
    }
    
    /**
     * 온도 데이터 로깅
     * @param data 센서 데이터
     * @param parsedTemperature 파싱된 온도
     * @param parser 사용된 파서
     * @param quality 파싱 품질 점수
     */
    public void logTemperatureData(SensorData data, double parsedTemperature, 
                                 String parser, double quality) {
        if (!shouldLog(LogType.TEMPERATURE_PARSING)) {
            return;
        }
        
        if (enableDetailedLogging) {
            logger.info("온도 파싱 완료: parser={}, 품질={:.2f}, 온도={}°C", 
                       parser, quality, parsedTemperature);
            
            logger.debug("온도 파싱 상세 정보:");
            logger.debug("  - 원본 데이터: rawTem={}, floatValue={}, signedInt32Value={}", 
                        data.getRawTem(), data.getFloatValue(), data.getSignedInt32Value());
            logger.debug("  - 파싱 결과: 온도={}°C, 파서={}, 품질={:.2f}", 
                        parsedTemperature, parser, quality);
            logger.debug("  - 타임스탬프: {}", getCurrentTimestamp());
        }
        
        incrementLogCounter(LogType.TEMPERATURE_PARSING);
    }
    
    /**
     * 센서 데이터 로깅
     * @param data 센서 데이터
     * @param sensorType 센서 타입
     */
    public void logSensorData(SensorData data, String sensorType) {
        if (!shouldLog(LogType.SENSOR_DATA)) {
            return;
        }
        
        if (enableDetailedLogging) {
            logger.info("센서 데이터 수신: 타입={}, rawTem={}, floatValue={}, signedInt32Value={}", 
                       sensorType, data.getRawTem(), data.getFloatValue(), data.getSignedInt32Value());
        }
        
        incrementLogCounter(LogType.SENSOR_DATA);
    }
    
    /**
     * 성능 로깅
     * @param operation 수행된 작업
     * @param startTime 시작 시간
     * @param endTime 종료 시간
     * @param additionalInfo 추가 정보
     */
    public void logPerformance(String operation, long startTime, long endTime, String additionalInfo) {
        if (!enablePerformanceLogging || !shouldLog(LogType.PERFORMANCE)) {
            return;
        }
        
        long duration = endTime - startTime;
        
        if (duration > 100) { // 100ms 이상 걸린 작업만 로깅
            logger.info("성능 로그: 작업={}, 소요시간={}ms, 정보={}", 
                       operation, duration, additionalInfo);
        } else {
            logger.debug("성능 로그: 작업={}, 소요시간={}ms, 정보={}", 
                        operation, duration, additionalInfo);
        }
        
        incrementLogCounter(LogType.PERFORMANCE);
    }
    
    /**
     * 에러 로깅
     * @param errorType 에러 타입
     * @param message 에러 메시지
     * @param throwable 발생한 예외
     * @param context 에러 발생 컨텍스트
     */
    public void logError(String errorType, String message, Throwable throwable, String context) {
        if (!enableErrorLogging || !shouldLog(LogType.ERROR)) {
            return;
        }
        
        String errorContext = String.format("에러 발생: 타입=%s, 컨텍스트=%s, 메시지=%s", 
                                          errorType, context, message);
        
        if (throwable != null) {
            logger.error(errorContext, throwable);
        } else {
            logger.error(errorContext);
        }
        
        incrementLogCounter(LogType.ERROR);
    }
    
    /**
     * 메모리 상태 로깅
     * @param memoryStatus 메모리 상태 정보
     */
    public void logMemoryStatus(String memoryStatus) {
        if (!shouldLog(LogType.MEMORY)) {
            return;
        }
        
        logger.info("메모리 상태: {}", memoryStatus);
        incrementLogCounter(LogType.MEMORY);
    }
    
    /**
     * 네트워크 작업 로깅
     * @param operation 네트워크 작업
     * @param status 작업 상태
     * @param details 상세 정보
     */
    public void logNetworkOperation(String operation, String status, String details) {
        if (!shouldLog(LogType.NETWORK)) {
            return;
        }
        
        logger.info("네트워크 작업: 작업={}, 상태={}, 상세={}", operation, status, details);
        incrementLogCounter(LogType.NETWORK);
    }
    
    /**
     * 로깅 여부 결정
     * @param logType 로그 타입
     * @return 로깅 여부
     */
    private boolean shouldLog(LogType logType) {
        String logTypeName = logType.name();
        Long lastLogTime = lastLogTimes.get(logTypeName);
        
        if (lastLogTime == null) {
            return true;
        }
        
        long timeSinceLastLog = System.currentTimeMillis() - lastLogTime;
        if (timeSinceLastLog < LOG_TIME_WINDOW) {
            // 로그 속도 제한 확인
            AtomicLong counter = logCounters.get(logTypeName);
            if (counter != null && counter.get() >= LOG_RATE_LIMIT) {
                return false;
            }
        } else {
            // 시간 윈도우 초기화
            logCounters.put(logTypeName, new AtomicLong(0));
        }
        
        return true;
    }
    
    /**
     * 로그 카운터 증가
     * @param logType 로그 타입
     */
    private void incrementLogCounter(LogType logType) {
        String logTypeName = logType.name();
        logCounters.computeIfAbsent(logTypeName, k -> new AtomicLong(0))
                  .incrementAndGet();
        lastLogTimes.put(logTypeName, System.currentTimeMillis());
    }
    
    /**
     * 현재 타임스탬프 반환
     * @return 현재 타임스탬프 문자열
     */
    private String getCurrentTimestamp() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
    }
    
    /**
     * 로깅 설정 업데이트
     * @param detailedLogging 상세 로깅 활성화 여부
     * @param performanceLogging 성능 로깅 활성화 여부
     * @param errorLogging 에러 로깅 활성화 여부
     */
    public void updateLoggingSettings(boolean detailedLogging, boolean performanceLogging, boolean errorLogging) {
        this.enableDetailedLogging = detailedLogging;
        this.enablePerformanceLogging = performanceLogging;
        this.enableErrorLogging = errorLogging;
        
        logger.info("로깅 설정 업데이트: 상세={}, 성능={}, 에러={}", 
                   detailedLogging, performanceLogging, errorLogging);
    }
    
    /**
     * 로깅 통계 정보 반환
     * @return 로깅 통계 정보
     */
    public Map<String, Long> getLoggingStatistics() {
        Map<String, Long> stats = new ConcurrentHashMap<>();
        logCounters.forEach((key, value) -> stats.put(key, value.get()));
        return stats;
    }
    
    /**
     * 로깅 통계 초기화
     */
    public void resetLoggingStatistics() {
        logCounters.clear();
        lastLogTimes.clear();
        logger.info("로깅 통계 초기화 완료");
    }
    
    /**
     * 로그 레벨 설정
     * @param logLevel 로그 레벨 (DEBUG, INFO, WARN, ERROR)
     */
    public void setLogLevel(String logLevel) {
        logger.info("로그 레벨 설정: {}", logLevel);
        // 실제 로그 레벨 설정은 SLF4J 설정 파일에서 관리
    }
    
    /**
     * 로그 파일 경로 설정
     * @param logFilePath 로그 파일 경로
     */
    public void setLogFilePath(String logFilePath) {
        logger.info("로그 파일 경로 설정: {}", logFilePath);
        // 실제 로그 파일 경로 설정은 SLF4J 설정 파일에서 관리
    }
}
