package com.sensor.bridge;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.jnrsmcu.sdk.netdevice.RSServer;
import com.jnrsmcu.sdk.netdevice.IDataListener;
import com.jnrsmcu.sdk.netdevice.NodeData;
import com.jnrsmcu.sdk.netdevice.RealTimeData;
import com.jnrsmcu.sdk.netdevice.LoginData;
import com.jnrsmcu.sdk.netdevice.StoreData;
import com.jnrsmcu.sdk.netdevice.TelecontrolAck;
import com.jnrsmcu.sdk.netdevice.TimmingAck;
import com.jnrsmcu.sdk.netdevice.ParamIdsData;
import com.jnrsmcu.sdk.netdevice.ParamData;
import com.jnrsmcu.sdk.netdevice.WriteParamAck;
import com.jnrsmcu.sdk.netdevice.TransDataAck;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

public class SensorBridge {
    private static final Logger logger = LoggerFactory.getLogger(SensorBridge.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build();

    private RSServer rsServer;
    private String goServerUrl;
    private String paramDatPath;
    private boolean isRunning = false;
    
    // 온도 필터링을 위한 이전 온도 값 저장
    private double previousTemperature = 0.0;
    
    // 온도 파싱 시스템
    private final TemperatureParser temperatureParser;
    
    public SensorBridge() {
        // 온도 파서 초기화
        this.temperatureParser = new TemperatureParser();
        logger.info("TemperatureParser 초기화 완료");
    }

    public static void main(String[] args) {
        SensorBridge bridge = new SensorBridge();
        bridge.start();
    }

    public void start() {
        try {
            loadConfiguration();
            initializeRSServer();
            isRunning = true;
            logger.info("Sensor Bridge started successfully");
            
            // 서버가 종료될 때까지 대기
            Runtime.getRuntime().addShutdownHook(new Thread(this::stop));
            
            while (isRunning) {
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            logger.error("Failed to start Sensor Bridge", e);
            System.exit(1);
        }
    }

    public void stop() {
        isRunning = false;
        if (rsServer != null) {
            rsServer.stop();
        }
        logger.info("Sensor Bridge stopped");
    }

    private void loadConfiguration() {
        Properties props = new Properties();
        try {
            props.load(getClass().getClassLoader().getResourceAsStream("application.properties"));
        } catch (IOException e) {
            logger.warn("Could not load application.properties, using default values");
        }

        // 환경변수 우선, 그 다음 properties 파일, 마지막 기본값
        goServerUrl = System.getenv("GO_SERVER_URL");
        if (goServerUrl == null || goServerUrl.isEmpty()) {
            goServerUrl = props.getProperty("go.server.url", "http://localhost:8080");
        }
        
        paramDatPath = props.getProperty("rs.server.param.file", "JavaSDKV2.2.2/param.dat");
        logger.info("Go server URL: {}", goServerUrl);
        logger.info("Param.dat path: {}", paramDatPath);
    }

    private void initializeRSServer() {
        try {
            logger.info("Loading param.dat from: {}", paramDatPath);
            
            // RSServer 초기화 (포트 8020 사용)
            rsServer = RSServer.Initiate(8020, paramDatPath);
            
            // 데이터 리스너 설정
            rsServer.addDataListener(new IDataListener() {
                @Override
                public void receiveRealtimeData(RealTimeData realTimeData) {
                    logger.info("Received realtime data");
                    // 실시간 데이터에서 NodeData 추출하여 처리
                    if (realTimeData != null) {
                        processRealTimeData(realTimeData);
                    }
                }

                @Override
                public void receiveLoginData(LoginData loginData) {
                    logger.info("Received login data");
                    // 로그인 데이터 처리
                }

                @Override
                public void receiveStoreData(StoreData storeData) {
                    logger.info("Received store data");
                    // 저장 데이터 처리
                }

                @Override
                public void receiveTelecontrolAck(TelecontrolAck telecontrolAck) {
                    logger.info("Received telecontrol ack");
                    // 원격 제어 응답 처리
                }

                @Override
                public void receiveTimmingAck(TimmingAck timmingAck) {
                    logger.info("Received timming ack");
                    // 타이밍 응답 처리
                }

                @Override
                public void receiveParamIds(ParamIdsData paramIdsData) {
                    logger.info("Received param ids");
                    // 파라미터 ID 목록 처리
                }

                @Override
                public void receiveParam(ParamData paramData) {
                    logger.info("Received param data");
                    // 파라미터 데이터 처리
                }

                @Override
                public void receiveWriteParamAck(WriteParamAck writeParamAck) {
                    logger.info("Received write param ack");
                    // 파라미터 쓰기 응답 처리
                }

                @Override
                public void receiveTransDataAck(TransDataAck transDataAck) {
                    logger.info("Received trans data ack");
                    // 데이터 전송 응답 처리
                }
            });

            rsServer.start();
            logger.info("RS Server started successfully");
        } catch (Exception e) {
            logger.error("Failed to initialize RS Server", e);
            throw new RuntimeException("Failed to initialize RS Server", e);
        }
    }

    private void processRealTimeData(RealTimeData realTimeData) {
        try {
            // RealTimeData 전체 정보 로깅
            logger.info("Raw RealTimeData: deviceId={}, lng={}, lat={}, coordinateType={}, relayStatus={}, nodeCount={}", 
                       realTimeData.getDeviceId(), realTimeData.getLng(), realTimeData.getLat(),
                       realTimeData.getCoordinateType(), realTimeData.getRelayStatus(),
                       realTimeData.getNodeList() != null ? realTimeData.getNodeList().size() : 0);
            
            // RealTimeData에서 센서 데이터 추출
            ExtendedSensorData extendedSensorData = new ExtendedSensorData();
            
            // 기본 정보 설정
            extendedSensorData.setDeviceId("sensor-" + realTimeData.getDeviceId());
            
            // NodeList에서 첫 번째 노드의 ID 사용
            int nodeId = 0;
            if (realTimeData.getNodeList() != null && !realTimeData.getNodeList().isEmpty()) {
                nodeId = realTimeData.getNodeList().get(0).getNodeId();
            }
            extendedSensorData.setNodeId(nodeId);
            extendedSensorData.setRecordedTime(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            
            if (realTimeData.getNodeList() != null && !realTimeData.getNodeList().isEmpty()) {
                NodeData firstNode = realTimeData.getNodeList().get(0);
                
                // 원본 데이터 로깅
                logger.info("Raw NodeData: nodeId={}, tem={}, hum={}, floatValue={}, signedInt32Value={}, unsignedInt32Value={}, recordTime={}", 
                           firstNode.getNodeId(), firstNode.getTem(), firstNode.getHum(), 
                           firstNode.getFloatValue(), firstNode.getSignedInt32Value(), 
                           firstNode.getUnSignedInt32Value(), firstNode.getRecordTime());
                
                // 원시 데이터 저장
                double rawTem = firstNode.getTem();
                double rawHum = firstNode.getHum();
                double floatValue = firstNode.getFloatValue();
                int signedInt32Value = firstNode.getSignedInt32Value();
                long unsignedInt32Value = firstNode.getUnSignedInt32Value();
                
                // 원시 데이터 설정
                extendedSensorData.setRawTem(rawTem);
                extendedSensorData.setRawHum(rawHum);
                extendedSensorData.setFloatValue(floatValue);
                extendedSensorData.setSignedInt32Value(signedInt32Value);
                extendedSensorData.setUnsignedInt32Value(unsignedInt32Value);
                
                // 다중 센서 데이터 파싱
                // 온도 파싱 - 새로운 파싱 시스템 사용
                double temperature = temperatureParser.parseTemperature(
                    new SensorData(rawTem, floatValue, signedInt32Value, unsignedInt32Value), 
                    null // 센서 타입 자동 감지
                );
                
                // 온도 유효성 검증 (5도 제한 로직 제거)
                if (temperature < -40.0 || temperature > 80.0) {
                    logger.warn("Temperature out of valid range: {}°C, using default value", temperature);
                    temperature = 25.0; // 기본값 사용
                }
                
                extendedSensorData.setTemperature(temperature);
                previousTemperature = temperature;
                
                logger.info("Temperature parsed: rawTem={}, floatValue={}, signedInt32Value={} -> {}°C", 
                           rawTem, floatValue, signedInt32Value, temperature);
                
                // 습도 파싱 (클라우드 대시보드 기준)
                double humidity = SensorDataParser.parseHumidity(rawHum);
                extendedSensorData.setHumidity(humidity);
                
                // PM10 파싱
                double pm10 = SensorDataParser.parsePM10(floatValue, signedInt32Value);
                extendedSensorData.setPm10(pm10);
                
                // PM2.5 파싱
                double pm25 = SensorDataParser.parsePM25(floatValue, signedInt32Value);
                extendedSensorData.setPm25(pm25);
                
                // 기압 파싱
                double pressure = SensorDataParser.parsePressure(signedInt32Value);
                extendedSensorData.setPressure(pressure);
                
                // 조도 파싱
                double illumination = SensorDataParser.parseIllumination(floatValue, unsignedInt32Value);
                extendedSensorData.setIllumination(illumination);
                
                // TVOC 파싱
                double tvoc = SensorDataParser.parseTVOC(floatValue, signedInt32Value);
                extendedSensorData.setTvoc(tvoc);
                
                // CO2 파싱
                double co2 = SensorDataParser.parseCO2(floatValue, signedInt32Value);
                extendedSensorData.setCo2(co2);
                
                // O2 파싱
                double o2 = SensorDataParser.parseO2(floatValue, unsignedInt32Value);
                extendedSensorData.setO2(o2);
                
                // CO 파싱
                double co = SensorDataParser.parseCO(floatValue, signedInt32Value);
                extendedSensorData.setCo(co);
            }
            
            // GPS 데이터 설정
            extendedSensorData.setLongitude(realTimeData.getLng());
            extendedSensorData.setLatitude(realTimeData.getLat());
            
            logger.info("Processed extended sensor data: deviceId={}, nodeId={}, temp={}, humidity={}, pm10={}, pm25={}, pressure={}, illumination={}, tvoc={}, co2={}, o2={}, co={}, lng={}, lat={}", 
                       extendedSensorData.getDeviceId(), extendedSensorData.getNodeId(), 
                       extendedSensorData.getTemperature(), extendedSensorData.getHumidity(),
                       extendedSensorData.getPm10(), extendedSensorData.getPm25(),
                       extendedSensorData.getPressure(), extendedSensorData.getIllumination(),
                       extendedSensorData.getTvoc(), extendedSensorData.getCo2(),
                       extendedSensorData.getO2(), extendedSensorData.getCo(),
                       extendedSensorData.getLongitude(), extendedSensorData.getLatitude());
            
            sendExtendedDataToGoServer(extendedSensorData);
        } catch (Exception e) {
            logger.error("Error processing realtime data", e);
        }
    }



    private void sendExtendedDataToGoServer(ExtendedSensorData extendedSensorData) {
        try {
            String json = objectMapper.writeValueAsString(extendedSensorData);
            logger.info("Sending extended data to Go server: {}", json);
            
            RequestBody body = RequestBody.create(
                json, 
                MediaType.get("application/json; charset=utf-8")
            );

            Request request = new Request.Builder()
                    .url(goServerUrl + "/api/sensor/extended-data")
                    .post(body)
                    .build();

            try (Response response = httpClient.newCall(request).execute()) {
                if (response.isSuccessful()) {
                    logger.info("Extended data sent successfully to Go server");
                } else {
                    logger.error("Failed to send extended data to Go server. Response code: {}, Body: {}", 
                               response.code(), response.body() != null ? response.body().string() : "null");
                }
            }
        } catch (Exception e) {
            logger.error("Error sending extended data to Go server", e);
        }
    }
} 