package vip.xiaonuo.coldchain.core.service.dataprocess.dataclean.impl;

import com.github.jfcloud.influxdb.util.InfluxDBDateFormatter;
import com.github.jfcloud.influxdb.util.InfluxdbTypeConverter;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import rk.netDevice.sdk.p2.NodeData;
import rk.netDevice.sdk.p2.RealTimeData;
import rk.netDevice.sdk.p2.StoreData;
import vip.xiaonuo.coldchain.core.bean.influxdb.DataType;
import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
import vip.xiaonuo.coldchain.core.event.MonitorDeviceHeartBeatEvent;
import vip.xiaonuo.coldchain.core.event.MonitorDeviceLoginEvent;
import vip.xiaonuo.coldchain.core.event.SensorDataEvent;
import vip.xiaonuo.coldchain.core.service.dataprocess.dataclean.MonitorDataProcessor;
import vip.xiaonuo.coldchain.core.service.dataprocess.model.RenKeColdChainMessageData;
import vip.xiaonuo.coldchain.modular.monitordevice.enums.DeviceModelEnum;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * Renke设备监控数据处理器
 * 该类用于处理来自Renke设备的实时数据，将其转换为标准的传感器数据并进行后续处理（如发布事件）。
 *
 * @author jackzhou
 * @version 1.0
 * @project jfcloud-coldchain
 * @date 2024/11/19
 */
@Slf4j  // 引入日志功能
public abstract class AbsRenkeMonitorDataProcessor implements MonitorDataProcessor<RenKeColdChainMessageData> {
    // 设备型号
    protected String modelName;
    // 事件发布器
    private ApplicationEventPublisher applicationEventPublisher;

    public AbsRenkeMonitorDataProcessor(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * 处理实时数据
     * 该方法是数据处理的入口，执行以下步骤：
     * 1. 校验数据是否有效
     * 2. 获取设备信息（如设备型号）
     * 3. 转换数据格式为传感器数据
     * 4. 发布传感器数据事件
     *
     * @param renKeColdChainMessageData 实时数据
     * @return 处理是否成功
     */
    @Override
    public Boolean processData(RenKeColdChainMessageData renKeColdChainMessageData) {
        // Step 1: 检查输入数据是否为 null
        if (Objects.isNull(renKeColdChainMessageData)) {
            log.error("接收到的处理数据为空.");
            return false;
        }
        // Step 2: 初始化数据和设备型号
        RealTimeData realTimeData = renKeColdChainMessageData.getRealTimeData();
        StoreData storeData = renKeColdChainMessageData.getStoreData();
        // 保存数据集合
        List<SensorData> sensorDataList = Lists.newArrayList();
        this.modelName = renKeColdChainMessageData.getModelName();
        // Step 3: 进行数据前处理
        preProcess(renKeColdChainMessageData);
        final String deviceId = String.valueOf(renKeColdChainMessageData.getDeviceId());
        // Step 4: 处理实时数据
        if (isValidRealTimeData(realTimeData)) {
            List<SensorData> realTimeSensorData = transRealTimeData2SensorDatas(realTimeData);
            if (realTimeSensorData != null && !realTimeSensorData.isEmpty()) {
                sensorDataList.addAll(realTimeSensorData);
            } else {
                log.warn("设备ID: {} 的实时数据中没有有效的传感器数据", deviceId);
            }
        }
        // Step 5: 处理缓存数据（存储数据）
        if (isValidStoreData(storeData)) {
            List<SensorData> storeSensorData = transStoreData2SensorDatas(storeData);
            if (storeSensorData != null && !storeSensorData.isEmpty()) {
                sensorDataList.addAll(storeSensorData);
            } else {
                log.warn("设备ID: {} 的缓存数据中没有有效的传感器数据", deviceId);
            }
        }
        boolean result = false;
        if (!sensorDataList.isEmpty()) {
            // Step 6: 保存传感器数据（实时数据 + 缓存数据）
            result = writeSensorDatas(sensorDataList);
            // Step 7: 输出处理结果日志，并在保存成功后进行后置处理
            if (result) {
                postProcess(sensorDataList);
            } else {
                log.error("设备ID: {}, 型号: {} 的数据处理失败", deviceId, modelName);
            }
        }
        return result;
    }

    // 校验实时数据是否有效
    private boolean isValidRealTimeData(RealTimeData data) {
        return Objects.nonNull(data) && Objects.nonNull(data.getNodeList()) && !data.getNodeList().isEmpty();
    }

    // 校验缓存数据是否有效
    private boolean isValidStoreData(StoreData data) {
        return Objects.nonNull(data) && Objects.nonNull(data.getNodeList()) && !data.getNodeList().isEmpty();
    }


    /**
     * 处理缓存数据
     *
     * @param storeData
     * @return
     */

    protected List<SensorData> transStoreData2SensorDatas(StoreData storeData) {
        List<SensorData> rlts = new ArrayList<>();
        int deviceId = storeData.getDeviceId();
        if (deviceId == 0 || storeData.getNodeList() == null || storeData.getNodeList().size() == 0) {
            return null;
        }
        List<NodeData> nodeList = storeData.getNodeList();
        for (NodeData nodeData : nodeList) {
            Date recordTime = nodeData.getRecordTime();
            int nodeId = nodeData.getNodeId();
            if (recordTime != null && nodeData.getCoordinateType() == 2) {
                SensorData sensorData = new SensorData();
                sensorData.setType(DataType.HISTORY.getCode());
                sensorData.setDeviceId(String.valueOf(deviceId));
                sensorData.setRoads(nodeId);
                sensorData.setModelName(modelName);
                sensorData.setCreateTime(InfluxDBDateFormatter.now(recordTime));
                sensorData.setTemperature(floatValue(nodeData.getTem()));
                sensorData.setHumidity(floatValue(nodeData.getHum()));
                sensorData.setTime(recordTime.toInstant());
                rlts.add(sensorData);
            }
        }
        return rlts;
    }

    /**
     * 数据前处理
     * 子类可以重写此方法执行特定的预处理操作，如数据校验、转换等。
     *
     * @param data 实时数据
     */
    protected void preProcess(RenKeColdChainMessageData data) {
        // 默认不做处理，子类可根据需求重写
    }

    /**
     * 数据后处理
     *
     * @param sensorDataList 实时数据
     */
    protected void postProcess(List<SensorData> sensorDataList) {
        // 默认不做处理，子类可根据需求重写
    }

    /**
     * 将实时数据转换为传感器数据列表
     * 该方法是抽象的，子类需要实现具体的转换逻辑。
     *
     * @param data 实时数据
     * @return 转换后的传感器数据列表
     */
    abstract List<SensorData> transRealTimeData2SensorDatas(RealTimeData data);

    /**
     * 发布单个传感器数据事件
     *
     * @param sensorData 需要发布的传感器数据
     * @return 是否发布成功
     */
    protected boolean writeSensorData(SensorData sensorData) {
        try {
            applicationEventPublisher.publishEvent(new SensorDataEvent(this, sensorData));
//            log.info("成功发布传感器数据事件: {}", sensorData);
            return true;
        } catch (Exception e) {
            log.error("保存传感器数据失败: {}", sensorData, e);
            return false;
        }
    }

    /**
     * 发布传感器数据事件列表
     * 待优化的功能  jackzhou-2024-1127
     *
     * @param sensorDataList 传感器数据列表
     * @return 是否成功发布所有事件
     */
    protected boolean writeSensorDatas(List<SensorData> sensorDataList) {
        if (sensorDataList.isEmpty()) {
            return false;
        }
        // 遍历传感器数据列表并发布每个数据的事件
        for (SensorData sensorData : sensorDataList) {
            boolean b = writeSensorData(sensorData);
            if (b) {
                RenKeColdChainMessageData renKeColdChainMessageData = new RenKeColdChainMessageData();
                renKeColdChainMessageData.setDeviceId(Integer.parseInt(sensorData.getDeviceId()));
//                log.info("发送心跳。。。。。。。。。。。。。。。。。。。。。。。。");
                heartbeat(renKeColdChainMessageData);
            } else {
                log.error("发布传感器数据事件失败: {}", sensorData);
                return false;
            }
        }
        return true;
    }

    /**
     * 获取设备型号
     * 该方法是抽象的，子类需要实现返回设备模型的逻辑。
     *
     * @return 设备型号
     */
    abstract DeviceModelEnum deviceModel();

    public String getModelName() {
        return deviceModel().getDeviceCode();
    }

    /**
     * 基础的SensorData
     *
     * @param data
     * @return
     */
    protected SensorData defaultSensorData(RealTimeData data) {
        final String deviceId = String.valueOf(data.getDeviceId());
        SensorData sensorData = new SensorData();
        // 如果记录时间为空，使用当前时间
        Instant defaultTime = Instant.now();
        sensorData.setTime(defaultTime);
        sensorData.setCreateTime(InfluxdbTypeConverter.instantStr(defaultTime));
        //设置模型型号
        float lat = data.getLat();
        float lng = data.getLng();
        double latDouble = lat;
        double lngDouble = lng;
        sensorData.setModelName(modelName);
        sensorData.setLat(latDouble);
        sensorData.setLng(lngDouble);
        sensorData.setDeviceId(deviceId);
        return sensorData;
    }

    // 公共的事件发布方法
    @SneakyThrows
    private void publishDeviceEvent(Integer deviceId, Date timestamp, Class<?> eventType) {
        if (timestamp == null) {
            // 默认使用当前时间
            timestamp = new Date();
        }
        applicationEventPublisher.publishEvent(eventType.getConstructor(Object.class, Integer.class, String.class, Date.class).newInstance(this, deviceId, null, timestamp));
    }

    @Override
    public void login(RenKeColdChainMessageData data) {
        Integer deviceId = data.getDeviceId();
        Date timestamp = new Date();
        publishDeviceEvent(deviceId, timestamp, MonitorDeviceLoginEvent.class);
    }

    @Override
    public void heartbeat(RenKeColdChainMessageData data) {
        Integer deviceId = data.getDeviceId();
        Date timestamp = new Date();
        publishDeviceEvent(deviceId, timestamp, MonitorDeviceHeartBeatEvent.class);
    }

    protected Float floatValue(float value) {
        if (value != 0.0) {
            return value;
        } else {
            return null;
        }
    }
}
