package com.sensor.bridge;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 센서 데이터 파싱 로직을 담당하는 클래스
 * 다양한 센서 타입의 데이터를 파싱하고 변환
 */
public class SensorDataParser {
    private static final Logger logger = LoggerFactory.getLogger(SensorDataParser.class);
    
    // 센서별 유효 범위 정의
    private static final double PM10_MIN = 0.0;
    private static final double PM10_MAX = 1000.0;
    private static final double PM25_MIN = 0.0;
    private static final double PM25_MAX = 500.0;
    private static final double PRESSURE_MIN = 800.0;
    private static final double PRESSURE_MAX = 1200.0;
    private static final double ILLUMINATION_MIN = 0.0;
    private static final double ILLUMINATION_MAX = 100000.0;
    private static final double TVOC_MIN = 0.0;
    private static final double TVOC_MAX = 10000.0;
    private static final double CO2_MIN = 300.0;
    private static final double CO2_MAX = 5000.0;
    private static final double O2_MIN = 15.0;
    private static final double O2_MAX = 25.0;
    private static final double CO_MIN = 0.0;
    private static final double CO_MAX = 100.0;
    private static final double TEMPERATURE_MIN = -40.0;
    private static final double TEMPERATURE_MAX = 80.0;
    private static final double HUMIDITY_MIN = 0.0;
    private static final double HUMIDITY_MAX = 100.0;
    
    /**
     * 센서 값 검증
     * @param value 검증할 값
     * @param min 최소값
     * @param max 최대값
     * @param sensorName 센서 이름 (로깅용)
     * @return 유효한 값인지 여부
     */
    private static boolean isValidValue(double value, double min, double max, String sensorName) {
        // NaN, 무한대, 매우 작은 값(e-40 등) 체크
        if (Double.isNaN(value) || Double.isInfinite(value) || Math.abs(value) < 1e-10) {
            logger.warn("{}: Invalid value detected: {}", sensorName, value);
            return false;
        }
        
        // 범위 체크
        if (value < min || value > max) {
            logger.warn("{}: Value out of range: {} (valid range: {} - {})", sensorName, value, min, max);
            return false;
        }
        
        return true;
    }
    
    /**
     * PM10 미세먼지 데이터 파싱 (개선된 버전)
     * @param floatValue float 값
     * @param signedInt32Value signed int32 값
     * @return 파싱된 PM10 값
     */
    public static double parsePM10(double floatValue, int signedInt32Value) {
        // floatValue가 유효한 범위인 경우 (PM10 전용 범위 체크)
        if (isValidValue(floatValue, PM10_MIN, PM10_MAX, "PM10")) {
            logger.debug("PM10 from floatValue: {}", floatValue);
            return floatValue;
        }
        
        // signedInt32Value에서 추출 (하위 16비트)
        double pm10 = (signedInt32Value & 0xFFFF) / 100.0;
        
        // 추출된 값 검증
        if (isValidValue(pm10, PM10_MIN, PM10_MAX, "PM10")) {
            logger.debug("PM10 from signedInt32Value: {} -> {}", signedInt32Value, pm10);
            return pm10;
        }
        
        logger.warn("PM10: All parsing methods failed, using default value");
        return 0.0;
    }
    
    /**
     * PM2.5 미세먼지 데이터 파싱 (개선된 버전)
     * @param floatValue float 값
     * @param signedInt32Value signed int32 값
     * @return 파싱된 PM2.5 값
     */
    public static double parsePM25(double floatValue, int signedInt32Value) {
        // floatValue가 유효한 범위인 경우 (PM2.5 전용 범위 체크)
        if (isValidValue(floatValue, PM25_MIN, PM25_MAX, "PM2.5")) {
            logger.debug("PM2.5 from floatValue: {}", floatValue);
            return floatValue;
        }
        
        // signedInt32Value에서 추출 (상위 16비트)
        double pm25 = ((signedInt32Value >> 16) & 0xFFFF) / 100.0;
        
        // 추출된 값 검증
        if (isValidValue(pm25, PM25_MIN, PM25_MAX, "PM2.5")) {
            logger.debug("PM2.5 from signedInt32Value: {} -> {}", signedInt32Value, pm25);
            return pm25;
        }
        
        logger.warn("PM2.5: All parsing methods failed, using default value");
        return 0.0;
    }
    
    /**
     * 기압 데이터 파싱 (개선된 버전)
     * @param signedInt32Value signed int32 값
     * @return 파싱된 기압 값 (hPa)
     */
    public static double parsePressure(int signedInt32Value) {
        // 상위 16비트에서 기압 데이터 추출
        int pressureRaw = (signedInt32Value >> 16) & 0xFFFF;
        double pressure = pressureRaw / 10.0 + 800.0; // 기본값 800hPa에서 시작
        
        // 추출된 값 검증
        if (isValidValue(pressure, PRESSURE_MIN, PRESSURE_MAX, "Pressure")) {
            logger.debug("Pressure from signedInt32Value: {} -> {} hPa", signedInt32Value, pressure);
            return pressure;
        }
        
        logger.warn("Pressure: Invalid value, using default value");
        return 1013.25; // 표준 대기압
    }
    
    /**
     * 조도 데이터 파싱 (개선된 버전)
     * @param floatValue float 값
     * @param unsignedInt32Value unsigned int32 값
     * @return 파싱된 조도 값 (lux)
     */
    public static double parseIllumination(double floatValue, long unsignedInt32Value) {
        // floatValue가 유효한 범위인 경우 (조도 전용 범위 체크)
        if (isValidValue(floatValue, ILLUMINATION_MIN, ILLUMINATION_MAX, "Illumination")) {
            logger.debug("Illumination from floatValue: {} lux", floatValue);
            return floatValue;
        }
        
        // unsignedInt32Value에서 추출
        double illumination = (unsignedInt32Value & 0xFFFF) * 10.0;
        
        // 추출된 값 검증
        if (isValidValue(illumination, ILLUMINATION_MIN, ILLUMINATION_MAX, "Illumination")) {
            logger.debug("Illumination from unsignedInt32Value: {} -> {} lux", unsignedInt32Value, illumination);
            return illumination;
        }
        
        logger.warn("Illumination: All parsing methods failed, using default value");
        return 0.0;
    }
    
    /**
     * TVOC (Total Volatile Organic Compounds) 데이터 파싱 (개선된 버전)
     * @param floatValue float 값
     * @param signedInt32Value signed int32 값
     * @return 파싱된 TVOC 값 (ppb)
     */
    public static double parseTVOC(double floatValue, int signedInt32Value) {
        // floatValue가 유효한 범위인 경우 (TVOC 전용 범위 체크)
        if (isValidValue(floatValue, TVOC_MIN, TVOC_MAX, "TVOC")) {
            logger.debug("TVOC from floatValue: {} ppb", floatValue);
            return floatValue;
        }
        
        // signedInt32Value에서 추출 (하위 16비트)
        double tvoc = (signedInt32Value & 0xFFFF);
        
        // 추출된 값 검증
        if (isValidValue(tvoc, TVOC_MIN, TVOC_MAX, "TVOC")) {
            logger.debug("TVOC from signedInt32Value: {} -> {} ppb", signedInt32Value, tvoc);
            return tvoc;
        }
        
        logger.warn("TVOC: All parsing methods failed, using default value");
        return 0.0;
    }
    
    /**
     * CO2 데이터 파싱 (개선된 버전)
     * @param floatValue float 값
     * @param signedInt32Value signed int32 값
     * @return 파싱된 CO2 값 (ppm)
     */
    public static double parseCO2(double floatValue, int signedInt32Value) {
        // floatValue가 유효한 범위인 경우 (CO2 전용 범위 체크)
        if (isValidValue(floatValue, CO2_MIN, CO2_MAX, "CO2")) {
            logger.debug("CO2 from floatValue: {} ppm", floatValue);
            return floatValue;
        }
        
        // signedInt32Value에서 추출 (상위 16비트)
        double co2 = ((signedInt32Value >> 16) & 0xFFFF) + 400; // 기본값 400ppm에서 시작
        
        // 추출된 값 검증
        if (isValidValue(co2, CO2_MIN, CO2_MAX, "CO2")) {
            logger.debug("CO2 from signedInt32Value: {} -> {} ppm", signedInt32Value, co2);
            return co2;
        }
        
        logger.warn("CO2: All parsing methods failed, using default value");
        return 400.0; // 대기 중 CO2 농도
    }
    
    /**
     * O2 데이터 파싱 (개선된 버전)
     * @param floatValue float 값
     * @param unsignedInt32Value unsigned int32 값
     * @return 파싱된 O2 값 (%)
     */
    public static double parseO2(double floatValue, long unsignedInt32Value) {
        // floatValue가 유효한 범위인 경우 (O2 전용 범위 체크)
        if (isValidValue(floatValue, O2_MIN, O2_MAX, "O2")) {
            logger.debug("O2 from floatValue: {}%", floatValue);
            return floatValue;
        }
        
        // unsignedInt32Value에서 추출 (상위 16비트)
        double o2 = ((unsignedInt32Value >> 16) & 0xFFFF) / 100.0;
        
        // 추출된 값 검증
        if (isValidValue(o2, O2_MIN, O2_MAX, "O2")) {
            logger.debug("O2 from unsignedInt32Value: {} -> {}%", unsignedInt32Value, o2);
            return o2;
        }
        
        logger.warn("O2: All parsing methods failed, using default value");
        return 20.9; // 대기 중 산소 농도
    }
    
    /**
     * CO 데이터 파싱 (개선된 버전)
     * @param floatValue float 값
     * @param signedInt32Value signed int32 값
     * @return 파싱된 CO 값 (ppm)
     */
    public static double parseCO(double floatValue, int signedInt32Value) {
        // floatValue가 유효한 범위인 경우 (CO 전용 범위 체크)
        if (isValidValue(floatValue, CO_MIN, CO_MAX, "CO")) {
            logger.debug("CO from floatValue: {} ppm", floatValue);
            return floatValue;
        }
        
        // signedInt32Value에서 추출 (하위 16비트)
        double co = (signedInt32Value & 0xFFFF) / 100.0;
        
        // 추출된 값 검증
        if (isValidValue(co, CO_MIN, CO_MAX, "CO")) {
            logger.debug("CO from signedInt32Value: {} -> {} ppm", signedInt32Value, co);
            return co;
        }
        
        logger.warn("CO: All parsing methods failed, using default value");
        return 0.0;
    }
    
    /**
     * 온도 데이터 파싱 (개선된 버전 - 5도 제한 로직 제거)
     * @param rawTem 원시 온도 값
     * @param floatValue float 값
     * @param signedInt32Value signed int32 값
     * @return 파싱된 온도 값 (°C)
     */
    public static double parseTemperature(double rawTem, double floatValue, int signedInt32Value) {
        // 1. signedInt32Value가 실제 온도인 경우 (대시보드: 29.1°C)
        if (signedInt32Value > 500000 && signedInt32Value < 600000) {
            // 524306을 온도로 변환: 524306 / 18000 ≈ 29.1°C
            double temp = signedInt32Value / 18000.0;
            if (isValidValue(temp, TEMPERATURE_MIN, TEMPERATURE_MAX, "Temperature")) {
                logger.debug("Temperature from signedInt32Value (dashboard match): {} -> {}°C", signedInt32Value, temp);
                return temp;
            }
        }
        
        // 2. rawTem이 0.1도 단위로 저장된 경우 (기존 rawTem * 10 공식)
        if (isValidValue(rawTem, 0, 50, "Raw Temperature")) {
            double temp = rawTem * 10;
            if (isValidValue(temp, TEMPERATURE_MIN, TEMPERATURE_MAX, "Temperature")) {
                logger.debug("Temperature conversion (rawTem * 10): {} -> {}°C", rawTem, temp);
                return temp;
            }
        }
        
        // 3. floatValue가 실제 온도인 경우
        if (isValidValue(floatValue, TEMPERATURE_MIN, TEMPERATURE_MAX, "Temperature")) {
            logger.debug("Using floatValue as temperature: {}°C", floatValue);
            return floatValue;
        }
        
        // 4. signedInt32Value에서 다른 패턴으로 온도 추출 시도
        if (signedInt32Value != 0) {
            // 다양한 스케일 팩터로 시도
            double[] scaleFactors = {1.0, 0.1, 0.01, 10.0, 100.0};
            for (double scale : scaleFactors) {
                double temp = signedInt32Value * scale;
                if (isValidValue(temp, TEMPERATURE_MIN, TEMPERATURE_MAX, "Temperature")) {
                    logger.debug("Temperature from signedInt32Value with scale {}: {} -> {}°C", scale, signedInt32Value, temp);
                    return temp;
                }
            }
        }
        
        // 5. 모든 방법 실패 시 기본값 반환
        logger.warn("Temperature: All parsing methods failed, using default value 25.0°C");
        logger.debug("Failed values - rawTem: {}, floatValue: {}, signedInt32Value: {}", 
                    rawTem, floatValue, signedInt32Value);
        return 25.0;
    }
    
    /**
     * 습도 데이터 파싱 (클라우드 대시보드 기준, 개선된 버전)
     * @param rawHum 원시 습도 값
     * @return 파싱된 습도 값 (%)
     */
    public static double parseHumidity(double rawHum) {
        // 대시보드 습도 35.0%에 맞춰 변환
        if (isValidValue(rawHum, 0, 10, "Raw Humidity")) {
            // 0.8 -> 35.0% 변환: 0.8 * 43.75 ≈ 35.0%
            double humidity = rawHum * 43.75;
            if (isValidValue(humidity, HUMIDITY_MIN, HUMIDITY_MAX, "Humidity")) {
                logger.debug("Humidity conversion (dashboard match): {} -> {}%", rawHum, humidity);
                return humidity;
            }
        } else if (isValidValue(rawHum, 10, 100, "Humidity")) {
            // 이미 적절한 범위인 경우
            logger.debug("Humidity already in range: {}", rawHum);
            return rawHum;
        }
        
        // 기본값
        logger.warn("Humidity: All parsing methods failed, using default value");
        return 60.0;
    }
} 