package com.sensor.bridge;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.Map;

/**
 * 에러 처리 강화 클래스
 * 센서 데이터 파싱 중 발생하는 에러를 체계적으로 처리
 */
public class ErrorHandler {
    
    private static final Logger logger = LoggerFactory.getLogger(ErrorHandler.class);
    
    // 에러 카운터 (에러 타입별)
    private final Map<String, AtomicLong> errorCounters = new ConcurrentHashMap<>();
    
    // 에러 발생 시간 기록 (에러 타입별)
    private final Map<String, Long> lastErrorTimes = new ConcurrentHashMap<>();
    
    // 에러 임계값 설정
    private static final int ERROR_THRESHOLD = 10; // 10초 내 에러 임계값
    private static final long ERROR_TIME_WINDOW = 10000; // 10초 (밀리초)
    
    // 에러 타입 정의
    public enum ErrorType {
        PARSING_ERROR("파싱 에러"),
        VALIDATION_ERROR("검증 에러"),
        SENSOR_ERROR("센서 에러"),
        NETWORK_ERROR("네트워크 에러"),
        MEMORY_ERROR("메모리 에러"),
        UNKNOWN_ERROR("알 수 없는 에러");
        
        private final String description;
        
        ErrorType(String description) {
            this.description = description;
        }
        
        public String getDescription() {
            return description;
        }
    }
    
    /**
     * 파싱 에러 처리
     * @param e 발생한 예외
     * @param data 센서 데이터
     * @return 기본값 또는 복구된 값
     */
    public double handleParsingError(Exception e, SensorData data) {
        logError(ErrorType.PARSING_ERROR, e, data);
        
        // 에러 카운터 증가
        incrementErrorCounter(ErrorType.PARSING_ERROR);
        
        // 에러 임계값 확인
        if (isErrorThresholdExceeded(ErrorType.PARSING_ERROR)) {
            logger.error("파싱 에러 임계값 초과! 시스템 상태 점검이 필요합니다.");
        }
        
        // 기본값 반환
        return getDefaultTemperature();
    }
    
    /**
     * 검증 에러 처리
     * @param data 센서 데이터
     * @param invalidValue 유효하지 않은 값
     * @return 검증된 값 또는 기본값
     */
    public double handleValidationError(SensorData data, double invalidValue) {
        logger.warn("검증 에러: 센서 데이터={}, 유효하지 않은 값={}", data, invalidValue);
        
        logError(ErrorType.VALIDATION_ERROR, null, data);
        incrementErrorCounter(ErrorType.VALIDATION_ERROR);
        
        // 값 범위 검증 및 수정
        return validateAndCorrectValue(invalidValue);
    }
    
    /**
     * 센서 에러 처리
     * @param sensorType 센서 타입
     * @param data 센서 데이터
     * @return 복구된 값 또는 기본값
     */
    public double handleSensorError(String sensorType, SensorData data) {
        logger.warn("센서 에러: 타입={}, 데이터={}", sensorType, data);
        
        logError(ErrorType.SENSOR_ERROR, null, data);
        incrementErrorCounter(ErrorType.SENSOR_ERROR);
        
        // 센서별 복구 로직
        return recoverFromSensorError(sensorType, data);
    }
    
    /**
     * 네트워크 에러 처리
     * @param e 네트워크 예외
     * @param operation 수행하려던 작업
     */
    public void handleNetworkError(Exception e, String operation) {
        logger.error("네트워크 에러: 작업={}", operation, e);
        
        logError(ErrorType.NETWORK_ERROR, e, null);
        incrementErrorCounter(ErrorType.NETWORK_ERROR);
        
        // 네트워크 재시도 로직 (필요시 구현)
        scheduleRetry(operation);
    }
    
    /**
     * 메모리 에러 처리
     * @param e 메모리 관련 예외
     */
    public void handleMemoryError(Exception e) {
        logger.error("메모리 에러 발생", e);
        
        logError(ErrorType.MEMORY_ERROR, e, null);
        incrementErrorCounter(ErrorType.MEMORY_ERROR);
        
        // 메모리 정리 수행
        performMemoryCleanup();
    }
    
    /**
     * 일반 에러 처리
     * @param e 발생한 예외
     * @param context 에러 발생 컨텍스트
     */
    public void handleGeneralError(Exception e, String context) {
        logger.error("일반 에러 발생: 컨텍스트={}", context, e);
        
        logError(ErrorType.UNKNOWN_ERROR, e, null);
        incrementErrorCounter(ErrorType.UNKNOWN_ERROR);
        
        // 에러 보고서 생성
        generateErrorReport(e, context);
    }
    
    /**
     * 에러 로깅
     * @param errorType 에러 타입
     * @param e 발생한 예외
     * @param data 관련 센서 데이터
     */
    private void logError(ErrorType errorType, Exception e, SensorData data) {
        String errorMessage = String.format("에러 발생: 타입=%s, 설명=%s", 
                                          errorType.name(), errorType.getDescription());
        
        if (e != null) {
            logger.error(errorMessage, e);
        } else {
            logger.error(errorMessage);
        }
        
        if (data != null) {
            logger.debug("관련 센서 데이터: {}", data);
        }
        
        // 에러 발생 시간 기록
        lastErrorTimes.put(errorType.name(), System.currentTimeMillis());
    }
    
    /**
     * 에러 카운터 증가
     * @param errorType 에러 타입
     */
    private void incrementErrorCounter(ErrorType errorType) {
        errorCounters.computeIfAbsent(errorType.name(), k -> new AtomicLong(0))
                    .incrementAndGet();
    }
    
    /**
     * 에러 임계값 초과 확인
     * @param errorType 에러 타입
     * @return 임계값 초과 여부
     */
    private boolean isErrorThresholdExceeded(ErrorType errorType) {
        Long lastErrorTime = lastErrorTimes.get(errorType.name());
        if (lastErrorTime == null) {
            return false;
        }
        
        long timeSinceLastError = System.currentTimeMillis() - lastErrorTime;
        return timeSinceLastError < ERROR_TIME_WINDOW;
    }
    
    /**
     * 값 검증 및 수정
     * @param value 검증할 값
     * @return 검증된 값
     */
    private double validateAndCorrectValue(double value) {
        // 온도 범위 검증 (-40°C ~ 80°C)
        if (value < -40.0) {
            logger.warn("온도 값이 너무 낮음: {}°C, -40°C로 수정", value);
            return -40.0;
        } else if (value > 80.0) {
            logger.warn("온도 값이 너무 높음: {}°C, 80°C로 수정", value);
            return 80.0;
        }
        
        return value;
    }
    
    /**
     * 센서 에러로부터 복구
     * @param sensorType 센서 타입
     * @param data 센서 데이터
     * @return 복구된 값
     */
    private double recoverFromSensorError(String sensorType, SensorData data) {
        // 센서별 복구 로직
        switch (sensorType != null ? sensorType.toUpperCase() : "UNKNOWN") {
            case "SHT30":
                // SHT30 센서 복구: floatValue 우선 사용
                if (data.getFloatValue() >= -40.0 && data.getFloatValue() <= 80.0) {
                    logger.info("SHT30 센서 복구: floatValue 사용");
                    return data.getFloatValue();
                }
                break;
                
            case "BME680":
                // BME680 센서 복구: signedInt32Value에서 추출
                if (data.getSignedInt32Value() != 0) {
                    double temp = data.getSignedInt32Value() / 100.0;
                    if (temp >= -40.0 && temp <= 80.0) {
                        logger.info("BME680 센서 복구: signedInt32Value에서 추출");
                        return temp;
                    }
                }
                break;
                
            default:
                // 기본 복구: rawTem * 10
                if (data.getRawTem() >= 0 && data.getRawTem() <= 50) {
                    logger.info("기본 센서 복구: rawTem * 10 사용");
                    return data.getRawTem() * 10;
                }
                break;
        }
        
        // 모든 복구 방법 실패 시 기본값 반환
        logger.warn("센서 복구 실패, 기본값 사용");
        return getDefaultTemperature();
    }
    
    /**
     * 재시도 스케줄링
     * @param operation 재시도할 작업
     */
    private void scheduleRetry(String operation) {
        // 재시도 로직 구현 (필요시)
        logger.info("작업 재시도 스케줄링: {}", operation);
    }
    
    /**
     * 메모리 정리 수행
     */
    private void performMemoryCleanup() {
        logger.info("메모리 정리 수행");
        System.gc();
    }
    
    /**
     * 에러 보고서 생성
     * @param e 발생한 예외
     * @param context 에러 발생 컨텍스트
     */
    private void generateErrorReport(Exception e, String context) {
        // 에러 보고서 생성 로직 (필요시 구현)
        logger.info("에러 보고서 생성: 컨텍스트={}", context);
    }
    
    /**
     * 기본 온도값 반환
     * @return 기본 온도값
     */
    private double getDefaultTemperature() {
        return 25.0; // 실내 온도
    }
    
    /**
     * 에러 통계 정보 반환
     * @return 에러 통계 정보
     */
    public Map<String, Long> getErrorStatistics() {
        Map<String, Long> stats = new ConcurrentHashMap<>();
        errorCounters.forEach((key, value) -> stats.put(key, value.get()));
        return stats;
    }
    
    /**
     * 에러 카운터 초기화
     */
    public void resetErrorCounters() {
        errorCounters.clear();
        lastErrorTimes.clear();
        logger.info("에러 카운터 초기화 완료");
    }
}
