package com.aiotagro.cattletrade.business.service;

import com.aiotagro.cattletrade.business.entity.*;
import com.aiotagro.cattletrade.business.mapper.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 设备预警检测服务
 * 在设备日志同步时进行预警检测
 */
@Service
public class DeviceWarningService {

    private static final Logger logger = LoggerFactory.getLogger(DeviceWarningService.class);

    @Autowired
    private DeliveryMapper deliveryMapper;

    @Autowired
    private WarningLogMapper warningLogMapper;

    @Autowired
    private XqClientLogMapper xqClientLogMapper;

    @Autowired
    private IotDeviceDataMapper iotDeviceDataMapper;

    /**
     * 检测设备预警
     * 每5分钟执行一次
     */
    public void checkDeviceWarnings() {
        try {
            logger.info("开始执行设备预警检测任务");

            // 查询所有未完成的运单 (status < 5)
            List<Delivery> activeDeliveries = deliveryMapper.selectList(
                new QueryWrapper<Delivery>()
                    .lt("status", 5)
            );

            if (activeDeliveries.isEmpty()) {
                logger.info("没有待检测的运单");
                return;
            }

            logger.info("找到 {} 个待检测运单", activeDeliveries.size());

            for (Delivery delivery : activeDeliveries) {
                try {
                    // 获取运单关联的设备
                    List<String> deviceIds = getDeliveryDevices(delivery.getId());
                    
                    // 如果运单没有绑定设备，跳过预警检测
                    if (deviceIds.isEmpty()) {
                        logger.debug("运单 {} 未绑定设备，跳过预警检测", delivery.getDeliveryNumber());
                        continue;
                    }
                    
                    logger.debug("运单 {} 有 {} 个设备，开始预警检测", delivery.getDeliveryNumber(), deviceIds.size());
                    
                    // 检测停留预警
                    checkStagnationWarning(delivery, deviceIds);
                    
                    // 检测温度预警
                    checkTemperatureWarning(delivery, deviceIds);
                    
                    // 检测距离偏离预警
                    checkDistanceDeviation(delivery, deviceIds);
                    
                    // 检测到达时间预警
                    checkArrivalTimeWarning(delivery, deviceIds);
                    
                } catch (Exception e) {
                    logger.error("检测运单 {} 预警失败", delivery.getDeliveryNumber(), e);
                }
            }

            logger.info("设备预警检测任务执行完成");
        } catch (Exception e) {
            logger.error("执行设备预警检测任务失败", e);
        }
    }

    /**
     * 检测停留预警 - 15分钟位置不动
     * 判断逻辑：查询设备最近15分钟的日志，如果经纬度坐标没有变化，则触发停留预警
     */
    private void checkStagnationWarning(Delivery delivery, List<String> deviceIds) {
        try {
            for (String deviceId : deviceIds) {
                logger.debug("检测设备 {} 停留预警", deviceId);
                
                // 查询最近15分钟的日志记录（每5分钟一条，共3条）
                List<XqClientLog> recentLogs = xqClientLogMapper.selectList(
                    new QueryWrapper<XqClientLog>()
                        .eq("device_id", deviceId)
                        .orderByDesc("update_time")
                        .last("LIMIT 3")
                );
                
                if (recentLogs.size() >= 3) {
                    // 获取最新记录的位置（停留位置）
                    XqClientLog latestLog = recentLogs.get(0);
                    String stayLat = latestLog.getLatitude();
                    String stayLon = latestLog.getLongitude();
                    
                    // 检查位置是否有效
                    if (stayLat == null || stayLon == null || 
                        stayLat.isEmpty() || stayLon.isEmpty()) {
                        logger.debug("设备 {} 位置信息无效，跳过停留预警检测", deviceId);
                        continue;
                    }
                    
                    // 检查最近3条记录的位置是否完全相同
                    boolean isStagnant = true;
                    for (int i = 1; i < recentLogs.size(); i++) {
                        String currentLat = recentLogs.get(i).getLatitude();
                        String currentLon = recentLogs.get(i).getLongitude();
                        
                        // 如果任何一条记录的位置不同，则不是停留状态
                        if (currentLat == null || currentLon == null ||
                            !stayLat.equals(currentLat) || !stayLon.equals(currentLon)) {
                            isStagnant = false;
                            logger.debug("设备 {} 位置发生变化，未触发停留预警", deviceId);
                            break;
                        }
                    }
                    
                    if (isStagnant) {
                        // ✅ 保存预警记录，包含停留位置的经纬度
                        String warningDetail = String.format(
                            "设备在15分钟内位置未发生变化，停留位置：纬度 %s，经度 %s",
                            stayLat, stayLon
                        );
                        
                        // 保存预警记录（包含经纬度信息）
                        saveWarningLogWithLocation(
                            delivery.getId(), 
                            "4", 
                            warningDetail,
                            stayLat,
                            stayLon,
                            deviceId
                        );
                        
                        logger.info("运单 {} 设备 {} 触发停留预警 - 位置: ({}, {})", 
                            delivery.getDeliveryNumber(), deviceId, stayLat, stayLon);
                    }
                } else {
                    logger.debug("设备 {} 日志记录不足3条，无法判断停留预警", deviceId);
                }
            }
        } catch (Exception e) {
            logger.error("检测停留预警失败", e);
        }
    }

    /**
     * 检测温度预警
     */
    private void checkTemperatureWarning(Delivery delivery, List<String> deviceIds) {
        try {
            for (String deviceId : deviceIds) {
                // 查询最新的设备日志
                XqClientLog latestLog = xqClientLogMapper.selectOne(
                    new QueryWrapper<XqClientLog>()
                        .eq("device_id", deviceId)
                        .orderByDesc("update_time")
                        .last("LIMIT 1")
                );
                
                if (latestLog != null && latestLog.getDeviceTemp() != null) {
                    try {
                        double temperature = Double.parseDouble(latestLog.getDeviceTemp());
                        
                        if (temperature > 40) {
                            saveWarningLog(delivery.getId(), "5", "高温预警：设备温度 " + temperature + "°C，超过40°C");
                            logger.info("运单 {} 设备 {} 触发高温预警", delivery.getDeliveryNumber(), deviceId);
                        } else if (temperature < 30) {
                            saveWarningLog(delivery.getId(), "6", "低温预警：设备温度 " + temperature + "°C，低于30°C");
                            logger.info("运单 {} 设备 {} 触发低温预警", delivery.getDeliveryNumber(), deviceId);
                        }
                    } catch (NumberFormatException e) {
                        logger.warn("设备温度格式错误: {}", latestLog.getDeviceTemp());
                    }
                }
            }
        } catch (Exception e) {
            logger.error("检测温度预警失败", e);
        }
    }

    /**
     * 检测距离偏离预警 - 当前位置偏离路线的十分之一
     */
    private void checkDistanceDeviation(Delivery delivery, List<String> deviceIds) {
        try {
            if (delivery.getStartLat() == null || delivery.getStartLon() == null ||
                delivery.getEndLat() == null || delivery.getEndLon() == null) {
                logger.debug("运单 {} 的起终点经纬度不完整，跳过距离检测", delivery.getDeliveryNumber());
                return;
            }
            
            // 计算起始地和目的地的距离
            double totalDistance = calculateDistance(
                Double.parseDouble(delivery.getStartLat()),
                Double.parseDouble(delivery.getStartLon()),
                Double.parseDouble(delivery.getEndLat()),
                Double.parseDouble(delivery.getEndLon())
            );
            
            double deviationThreshold = totalDistance / 10; // 十分之一的距离
            
            for (String deviceId : deviceIds) {
                // 查询最新的设备日志
                XqClientLog latestLog = xqClientLogMapper.selectOne(
                    new QueryWrapper<XqClientLog>()
                        .eq("device_id", deviceId)
                        .orderByDesc("update_time")
                        .last("LIMIT 1")
                );
                
                if (latestLog != null && latestLog.getLatitude() != null && latestLog.getLongitude() != null) {
                    try {
                        double currentLat = Double.parseDouble(latestLog.getLatitude());
                        double currentLon = Double.parseDouble(latestLog.getLongitude());
                        
                        // 计算当前位置到起点的距离
                        double distanceFromStart = calculateDistance(
                            Double.parseDouble(delivery.getStartLat()),
                            Double.parseDouble(delivery.getStartLon()),
                            currentLat,
                            currentLon
                        );
                        
                        // 计算当前位置到终点的距离
                        double distanceFromEnd = calculateDistance(
                            currentLat,
                            currentLon,
                            Double.parseDouble(delivery.getEndLat()),
                            Double.parseDouble(delivery.getEndLon())
                        );
                        
                        // 计算实际路线距离（当前位置到起点的距离 + 当前位置到终点的距离）
                        double actualRouteDistance = distanceFromStart + distanceFromEnd;
                        
                        // 如果实际路线距离超出总距离 + 偏离阈值，触发预警
                        if (actualRouteDistance > totalDistance + deviationThreshold) {
                            saveWarningLog(delivery.getId(), "7", 
                                String.format("位置偏离预警：当前位置偏离计划路线超过 %.2f 公里", deviationThreshold));
                            logger.info("运单 {} 设备 {} 触发位置偏离预警", delivery.getDeliveryNumber(), deviceId);
                        }
                    } catch (NumberFormatException e) {
                        logger.warn("经纬度格式错误");
                    }
                }
            }
        } catch (Exception e) {
            logger.error("检测距离偏离预警失败", e);
        }
    }

    /**
     * 检测到达时间预警
     */
    private void checkArrivalTimeWarning(Delivery delivery, List<String> deviceIds) {
        try {
            if (delivery.getEstimatedDeliveryTime() == null) {
                logger.debug("运单 {} 没有预计送达时间，跳过到达时间检测", delivery.getDeliveryNumber());
                return;
            }
            
            Date estimatedTime = delivery.getEstimatedDeliveryTime();
            Date currentTime = new Date();
            
            // 计算剩余时间（小时）
            long diffInMillis = estimatedTime.getTime() - currentTime.getTime();
            double remainingHours = diffInMillis / (1000.0 * 60 * 60);
            
            if (deviceIds.isEmpty()) {
                logger.debug("运单 {} 没有设备，跳过到达时间检测", delivery.getDeliveryNumber());
                return;
            }
            
            // 查询最新位置
            XqClientLog latestLog = xqClientLogMapper.selectOne(
                new QueryWrapper<XqClientLog>()
                    .in("device_id", deviceIds)
                    .orderByDesc("update_time")
                    .last("LIMIT 1")
            );
            
            if (latestLog != null && latestLog.getLatitude() != null && latestLog.getLongitude() != null) {
                try {
                    double currentLat = Double.parseDouble(latestLog.getLatitude());
                    double currentLon = Double.parseDouble(latestLog.getLongitude());
                    
                    if (delivery.getEndLat() == null || delivery.getEndLon() == null) {
                        return;
                    }
                    
                    double endLat = Double.parseDouble(delivery.getEndLat());
                    double endLon = Double.parseDouble(delivery.getEndLon());
                    
                    // 计算剩余距离（公里）
                    double remainingDistance = calculateDistance(currentLat, currentLon, endLat, endLon);
                    
                    // 假设平均速度80公里/小时
                    double avgSpeed = 80;
                    double requiredTime = remainingDistance / avgSpeed;
                    
                    // 判断是否能在预计时间内到达
                    if (remainingHours > 0 && requiredTime > remainingHours) {
                        // 延误预警
                        saveWarningLog(delivery.getId(), "8", 
                            String.format("延误预警：预计无法在约定时间到达，预计延误 %.2f 小时", requiredTime - remainingHours));
                        logger.info("运单 {} 触发延误预警", delivery.getDeliveryNumber());
                    } else if (remainingHours < 0 && Math.abs(remainingHours) > 2) {
                        // 超前到达预警（超过2小时）
                        saveWarningLog(delivery.getId(), "9", 
                            String.format("超前到达预警：已提前 %.2f 小时到达目的地", Math.abs(remainingHours)));
                        logger.info("运单 {} 触发超前到达预警", delivery.getDeliveryNumber());
                    }
                } catch (NumberFormatException e) {
                    logger.warn("经纬度格式错误");
                }
            }
        } catch (Exception e) {
            logger.error("检测到达时间预警失败", e);
        }
    }

    /**
     * 获取运单关联的设备ID列表（从iot_device_data表查询）
     */
    private List<String> getDeliveryDevices(Integer deliveryId) {
        try {
            List<IotDeviceData> devices = iotDeviceDataMapper.selectList(
                new QueryWrapper<IotDeviceData>()
                    .eq("delivery_id", deliveryId)
            );
            
            List<String> deviceIds = new ArrayList<>();
            for (IotDeviceData device : devices) {
                if (device.getDeviceId() != null && !device.getDeviceId().isEmpty()) {
                    deviceIds.add(device.getDeviceId());
                }
            }
            
            return deviceIds;
        } catch (Exception e) {
            logger.error("查询运单{}的设备失败", deliveryId, e);
            return new ArrayList<>();
        }
    }

    /**
     * 保存预警记录
     */
    /**
     * 保存预警记录（带位置信息）
     * 用于停留预警、位置偏离预警等需要记录具体位置的预警类型
     */
    private void saveWarningLogWithLocation(Integer deliveryId, String warningType, 
                                           String warningDetail, String latitude, 
                                           String longitude, String deviceId) {
        WarningLog warningLog = new WarningLog();
        warningLog.setDeliveryId(deliveryId);
        warningLog.setWarningType(warningType);
        warningLog.setWarningTime(new Date());
        warningLog.setWarningDetail(warningDetail);
        
        // ✅ 保存停留位置的经纬度
        warningLog.setLatitude(latitude);
        warningLog.setLongitude(longitude);
        
        // ✅ 保存触发预警的设备ID
        warningLog.setDeviceId(deviceId);
        
        // 验证是否已有相同类型的最新预警（5分钟内不重复保存）
        WarningLog latestWarning = warningLogMapper.selectOne(
            new QueryWrapper<WarningLog>()
                .eq("delivery_id", deliveryId)
                .eq("warning_type", warningType)
                .orderByDesc("warning_time")
                .last("LIMIT 1")
        );
        
        if (latestWarning != null) {
            long diffMinutes = (new Date().getTime() - latestWarning.getWarningTime().getTime()) / (1000 * 60);
            if (diffMinutes < 5) {
                logger.debug("5分钟内已有相同预警，跳过保存");
                return;
            }
        }
        
        warningLogMapper.insert(warningLog);
        logger.info("保存预警记录：运单ID={}, 预警类型={}, 位置=({}, {})", 
            deliveryId, warningType, latitude, longitude);
    }
    
    /**
     * 保存预警记录（不带位置信息）
     * 用于温度预警、延误预警等不需要记录具体位置的预警类型
     */
    private void saveWarningLog(Integer deliveryId, String warningType, String warningDesc) {
        WarningLog warningLog = new WarningLog();
        warningLog.setDeliveryId(deliveryId);
        warningLog.setWarningType(warningType);
        warningLog.setWarningTime(new Date());
        
        // 验证是否已有相同类型的最新预警（5分钟内不重复保存）
        WarningLog latestWarning = warningLogMapper.selectOne(
            new QueryWrapper<WarningLog>()
                .eq("delivery_id", deliveryId)
                .eq("warning_type", warningType)
                .orderByDesc("warning_time")
                .last("LIMIT 1")
        );
        
        if (latestWarning != null) {
            long diffMinutes = (new Date().getTime() - latestWarning.getWarningTime().getTime()) / (1000 * 60);
            if (diffMinutes < 5) {
                logger.debug("5分钟内已有相同预警，跳过保存");
                return;
            }
        }
        
        warningLogMapper.insert(warningLog);
        logger.info("保存预警记录：运单ID={}, 预警类型={}", deliveryId, warningType);
    }

    /**
     * 计算两点间距离（Haversine公式）
     */
    private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        final int R = 6371; // 地球半径（公里）
        
        double lat1Rad = Math.toRadians(lat1);
        double lat2Rad = Math.toRadians(lat2);
        double deltaLat = Math.toRadians(lat2 - lat1);
        double deltaLon = Math.toRadians(lon2 - lon1);
        
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
                   Math.cos(lat1Rad) * Math.cos(lat2Rad) *
                   Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        
        return R * c;
    }

    /**
     * 输出统计信息
     */
    public void logStatistics() {
        logger.info("设备预警检测统计信息已更新");
    }
}

