package com.aiotagro.cattletrade.business.service;

import com.aiotagro.cattletrade.business.dto.yingyan.YingyanTrackPoint;
import com.aiotagro.cattletrade.business.entity.Delivery;
import com.aiotagro.cattletrade.business.entity.IotDeviceData;
import com.aiotagro.cattletrade.business.entity.JbqClientLog;
import com.aiotagro.cattletrade.business.entity.JbqServerLog;
import com.aiotagro.cattletrade.business.entity.XqClientLog;
import com.aiotagro.cattletrade.business.mapper.IotDeviceDataMapper;
import com.aiotagro.cattletrade.business.mapper.JbqClientLogMapper;
import com.aiotagro.cattletrade.business.mapper.JbqServerLogMapper;
import com.aiotagro.cattletrade.business.mapper.XqClientLogMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 运送清单鹰眼同步服务
 */
@Service
public class DeliveryYingyanSyncService {

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

    /**
     * 每次最多同步 120 个轨迹点，避免请求过多
     */
    private static final int MAX_SYNC_POINTS = 120;

    private static final double ARRIVAL_RADIUS_METERS = 500D;

    @Autowired
    private IDeliveryService deliveryService;

    @Autowired
    private IotDeviceDataMapper iotDeviceDataMapper;

    @Autowired
    private JbqClientLogMapper jbqClientLogMapper;

    @Autowired
    private JbqServerLogMapper jbqServerLogMapper;

    @Autowired
    private XqClientLogMapper xqClientLogMapper;

    @Autowired
    private BaiduYingyanService baiduYingyanService;

    /**
     * 同步所有运输中的运送清单
     */
    public void syncActiveDeliveries() {
        logger.info("========== 开始执行百度鹰眼轨迹同步任务 ==========");
        LambdaQueryWrapper<Delivery> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Delivery::getStatus, 2);
        wrapper.isNotNull(Delivery::getLicensePlate);
        List<Delivery> deliveries = deliveryService.list(wrapper);
        if (CollectionUtils.isEmpty(deliveries)) {
            logger.info("未找到运输中的运单（status=2），跳过同步");
            return;
        }

        logger.info("找到 {} 个运输中的运单，开始同步轨迹", deliveries.size());
        int successCount = 0;
        int failCount = 0;
        int noPointsCount = 0;

        for (Delivery delivery : deliveries) {
            try {
                int result = syncDelivery(delivery);
                if (result > 0) {
                    successCount++;
                    logger.info("运单 {} 同步成功，上传了 {} 个轨迹点", delivery.getDeliveryNumber(), result);
                } else if (result == 0) {
                    noPointsCount++;
                    logger.debug("运单 {} 无有效轨迹点可同步", delivery.getDeliveryNumber());
                } else {
                    failCount++;
                }
            } catch (Exception e) {
                failCount++;
                logger.error("运单 {} 同步百度鹰眼失败", delivery.getDeliveryNumber(), e);
            }
        }

        logger.info("百度鹰眼轨迹同步完成 - 成功: {}, 无轨迹点: {}, 失败: {}", successCount, noPointsCount, failCount);
        logger.info("========== 百度鹰眼轨迹同步任务执行完成 ==========");
    }

    private int syncDelivery(Delivery delivery) {
        String entityName = resolveEntityName(delivery);
        if (StringUtils.isBlank(entityName)) {
            logger.warn("运单 {} 无法同步，车牌为空", delivery.getDeliveryNumber());
            return -1;
        }

        logger.info("开始同步运单 {} 的轨迹，终端名称: {}", delivery.getDeliveryNumber(), entityName);

        // ✅ 确保终端存在（如果已存在则继续，如果不存在则创建）
        boolean entityExists = baiduYingyanService.ensureEntity(entityName);
        if (!entityExists) {
            logger.warn("运单 {} 创建/确保终端失败，终端名称: {}，但继续尝试上传轨迹点（终端可能已存在）", 
                delivery.getDeliveryNumber(), entityName);
            // 注意：即使 ensureEntity 返回 false，也继续尝试上传轨迹点
            // 因为终端可能已经存在，只是创建时返回了错误状态码
        } else {
            logger.debug("运单 {} 终端已存在或创建成功，终端名称: {}", delivery.getDeliveryNumber(), entityName);
        }

        Date startTime = determineSyncStart(delivery);
        Date endTime = new Date();
        logger.info("运单 {} 查询轨迹点时间范围: {} 至 {}", 
            delivery.getDeliveryNumber(), startTime, endTime);

        List<YingyanTrackPoint> points = collectTrackPoints(delivery, startTime, endTime);
        if (CollectionUtils.isEmpty(points)) {
            logger.warn("运单 {} 未找到有效轨迹点，终端名称: {}, 时间范围: {} 至 {}", 
                delivery.getDeliveryNumber(), entityName, startTime, endTime);
            
            // ✅ 详细排查原因：从 iot_device_data 表查询设备
            QueryWrapper<IotDeviceData> deviceQueryWrapper = new QueryWrapper<>();
            deviceQueryWrapper.eq("delivery_id", delivery.getId());
            deviceQueryWrapper.and(wrapper -> wrapper.eq("is_delet", 0).or().isNull("is_delet"));
            List<IotDeviceData> devices = iotDeviceDataMapper.selectList(deviceQueryWrapper);
            
            logger.warn("运单 {} 在 iot_device_data 表中的设备数量: {}", 
                delivery.getDeliveryNumber(), devices != null ? devices.size() : 0);
            
            if (devices != null && !devices.isEmpty()) {
                devices.forEach(device -> {
                    logger.warn("运单 {} 设备信息 - deviceId: {}, deviceType: {}, latitude: {}, longitude: {}", 
                        delivery.getDeliveryNumber(), device.getDeviceId(), device.getDeviceType(),
                        device.getLatitude(), device.getLongitude());
                });
            } else {
                logger.warn("运单 {} 在 iot_device_data 表中没有找到设备，deliveryId: {}", 
                    delivery.getDeliveryNumber(), delivery.getId());
            }
            
            return 0;
        }

        logger.info("运单 {} 找到 {} 个有效轨迹点，开始批量上传到百度鹰眼", 
            delivery.getDeliveryNumber(), points.size());

        // ✅ 检查运单状态，如果已结束则停止同步
        if (delivery.getStatus() != null && delivery.getStatus() == 3) {
            logger.info("运单 {} 状态已变为已结束，停止同步", delivery.getDeliveryNumber());
            return 0;
        }

        // ✅ 使用批量上传接口
        int successCount = baiduYingyanService.pushTrackPoints(entityName, points);
        int failCount = points.size() - successCount;

        if (successCount > 0) {
            // ✅ 批量上传成功后，使用最后一个轨迹点的时间更新同步时间
            YingyanTrackPoint lastPoint = points.get(points.size() - 1);
            Date locDate = new Date(lastPoint.getLocTime() * 1000L);
            updateLastSync(delivery, locDate);
            
            // ✅ 检查是否到达目的地（使用最后一个轨迹点）
            handleArrivalIfNeeded(delivery, lastPoint);
            
            logger.info("运单 {} 批量上传轨迹点成功 - 成功: {}, 失败: {}, 总计: {}, 最后时间: {}", 
                delivery.getDeliveryNumber(), successCount, failCount, points.size(), locDate);
        } else {
            logger.warn("运单 {} 批量上传轨迹点全部失败 - 总计: {}", 
                delivery.getDeliveryNumber(), points.size());
        }

        return successCount;
    }

    private List<YingyanTrackPoint> collectTrackPoints(Delivery delivery, Date startTime, Date endTime) {
        // ✅ 修改：从 iot_device_data 表查询设备，而不是 delivery_device 表
        QueryWrapper<IotDeviceData> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("delivery_id", delivery.getId());
        queryWrapper.and(wrapper -> wrapper.eq("is_delet", 0).or().isNull("is_delet")); // 只查询未删除的设备
        List<IotDeviceData> devices = iotDeviceDataMapper.selectList(queryWrapper);
        
        if (CollectionUtils.isEmpty(devices)) {
            logger.warn("运单 {} 在 iot_device_data 表中未找到设备，deliveryId: {}", 
                delivery.getDeliveryNumber(), delivery.getId());
            return Collections.emptyList();
        }

        logger.info("运单 {} 从 iot_device_data 表查询到 {} 个设备", 
            delivery.getDeliveryNumber(), devices.size());

        // ✅ 按设备类型分组：1=主机，2=耳标，4=项圈
        Map<Integer, List<String>> deviceMap = devices.stream()
            .filter(item -> StringUtils.isNotBlank(item.getDeviceId()))
            .collect(Collectors.groupingBy(device -> {
                Integer deviceType = device.getDeviceType();
                // 设备类型：1=主机，2=耳标，4=项圈
                return deviceType == null ? 0 : deviceType;
            }, Collectors.mapping(IotDeviceData::getDeviceId, Collectors.toList())));

        // ✅ 记录设备分组情况
        logger.debug("运单 {} 设备分组 - 主机(1): {}, 耳标(2): {}, 项圈(4): {}", 
            delivery.getDeliveryNumber(),
            deviceMap.getOrDefault(1, Collections.emptyList()).size(),
            deviceMap.getOrDefault(2, Collections.emptyList()).size(),
            deviceMap.getOrDefault(4, Collections.emptyList()).size());

        List<YingyanTrackPoint> result = new ArrayList<>();
        int remaining = MAX_SYNC_POINTS;

        // 1=主机设备，查询 jbq_server_log 表
        remaining = appendServerLogs(deviceMap.get(1), startTime, endTime, remaining, result);
        if (remaining <= 0) {
            return sortPoints(result);
        }

        // 2=耳标设备，查询 jbq_client_log 表
        remaining = appendEarTagLogs(deviceMap.get(2), startTime, endTime, remaining, result);
        if (remaining <= 0) {
            return sortPoints(result);
        }

        // 4=项圈设备，查询 xq_client_log 表
        appendCollarLogs(deviceMap.get(4), startTime, endTime, remaining, result);
        return sortPoints(result);
    }

    private int appendServerLogs(List<String> deviceIds, Date startTime, Date endTime, int remaining, List<YingyanTrackPoint> result) {
        if (CollectionUtils.isEmpty(deviceIds) || remaining <= 0) {
            return remaining;
        }
        List<JbqServerLog> logs = jbqServerLogMapper.listLogsForYingyan(deviceIds, startTime, endTime, remaining);
        if (CollectionUtils.isEmpty(logs)) {
            return remaining;
        }
        logs.forEach(log -> {
            YingyanTrackPoint point = convertToPoint(log.getLatitude(), log.getLongitude(), log.getUpdateTime());
            if (point != null) {
                result.add(point);
            }
        });
        return Math.max(0, remaining - logs.size());
    }

    private int appendEarTagLogs(List<String> deviceIds, Date startTime, Date endTime, int remaining, List<YingyanTrackPoint> result) {
        if (CollectionUtils.isEmpty(deviceIds) || remaining <= 0) {
            return remaining;
        }
        List<JbqClientLog> logs = jbqClientLogMapper.listLogsForYingyan(deviceIds, startTime, endTime, remaining);
        if (CollectionUtils.isEmpty(logs)) {
            return remaining;
        }
        logs.forEach(log -> {
            YingyanTrackPoint point = convertToPoint(log.getLatitude(), log.getLongitude(), log.getUpdateTime());
            if (point != null) {
                result.add(point);
            }
        });
        return Math.max(0, remaining - logs.size());
    }

    private void appendCollarLogs(List<String> deviceIds, Date startTime, Date endTime, int remaining, List<YingyanTrackPoint> result) {
        if (CollectionUtils.isEmpty(deviceIds) || remaining <= 0) {
            return;
        }
        List<XqClientLog> logs = xqClientLogMapper.listLogsForYingyan(deviceIds, startTime, endTime, remaining);
        if (CollectionUtils.isEmpty(logs)) {
            return;
        }
        logs.forEach(log -> {
            YingyanTrackPoint point = convertToPoint(log.getLatitude(), log.getLongitude(), log.getUpdateTime());
            if (point != null) {
                result.add(point);
            }
        });
    }

    private List<YingyanTrackPoint> sortPoints(List<YingyanTrackPoint> points) {
        if (CollectionUtils.isEmpty(points)) {
            return Collections.emptyList();
        }
        return points.stream()
            .sorted(Comparator.comparingLong(YingyanTrackPoint::getLocTime))
            .collect(Collectors.toList());
    }

    private YingyanTrackPoint convertToPoint(String latStr, String lonStr, Date time) {
        if (StringUtils.isAnyBlank(latStr, lonStr) || time == null) {
            logger.debug("转换轨迹点失败：经纬度或时间为空 - lat={}, lon={}, time={}", latStr, lonStr, time);
            return null;
        }
        try {
            double lat = Double.parseDouble(latStr);
            double lon = Double.parseDouble(lonStr);
            
            // ✅ 详细验证坐标有效性
            if (lat == 0 && lon == 0) {
                logger.debug("转换轨迹点失败：经纬度为 (0,0) - lat={}, lon={}", latStr, lonStr);
                return null;
            }
            
            if (!isValidCoordinate(lat, lon)) {
                logger.debug("转换轨迹点失败：经纬度超出有效范围 - lat={}, lon={}", lat, lon);
                return null;
            }
            
            YingyanTrackPoint point = new YingyanTrackPoint();
            point.setLatitude(lat);
            point.setLongitude(lon);
            point.setLocTime(time.getTime() / 1000);
            
            logger.debug("转换轨迹点成功 - lat={}, lon={}, time={}", lat, lon, time);
            return point;
        } catch (NumberFormatException ex) {
            logger.warn("解析经纬度失败: lat={}, lon={}, error={}", latStr, lonStr, ex.getMessage());
            return null;
        }
    }

    private boolean isValidCoordinate(double lat, double lon) {
        return lat >= -90 && lat <= 90 && lon >= -180 && lon <= 180;
    }

    private String resolveEntityName(Delivery delivery) {
        if (StringUtils.isNotBlank(delivery.getYingyanEntityName())) {
            return delivery.getYingyanEntityName().trim();
        }
        if (StringUtils.isNotBlank(delivery.getLicensePlate())) {
            return delivery.getLicensePlate().trim();
        }
        return null;
    }

    private Date determineSyncStart(Delivery delivery) {
        if (delivery.getYingyanLastSyncTime() != null) {
            return delivery.getYingyanLastSyncTime();
        }
        if (delivery.getEstimatedDeliveryTime() != null) {
            return delivery.getEstimatedDeliveryTime();
        }
        if (delivery.getEstimatedDepartureTime() != null) {
            return delivery.getEstimatedDepartureTime();
        }
        if (delivery.getCreateTime() != null) {
            return delivery.getCreateTime();
        }
        return new Date(System.currentTimeMillis() - Duration.ofHours(12).toMillis());
    }

    private void updateLastSync(Delivery delivery, Date newTime) {
        if (newTime == null) {
            return;
        }
        Delivery update = new Delivery();
        update.setId(delivery.getId());
        update.setYingyanLastSyncTime(newTime);
        deliveryService.updateById(update);
        delivery.setYingyanLastSyncTime(newTime);
    }

    private void handleArrivalIfNeeded(Delivery delivery, YingyanTrackPoint point) {
        if (delivery.getStatus() == null || delivery.getStatus() != 2) {
            return;
        }
        if (StringUtils.isAnyBlank(delivery.getEndLat(), delivery.getEndLon())) {
            return;
        }
        try {
            double targetLat = Double.parseDouble(delivery.getEndLat());
            double targetLon = Double.parseDouble(delivery.getEndLon());
            double distance = calculateDistance(targetLat, targetLon, point.getLatitude(), point.getLongitude());
            if (distance <= ARRIVAL_RADIUS_METERS) {
                Delivery update = new Delivery();
                update.setId(delivery.getId());
                update.setStatus(3);
                Date arrivalTime = new Date(point.getLocTime() * 1000L);
                update.setArrivalTime(arrivalTime);
                update.setYingyanLastSyncTime(arrivalTime);
                deliveryService.updateById(update);

                delivery.setStatus(3);
                delivery.setArrivalTime(arrivalTime);
                delivery.setYingyanLastSyncTime(arrivalTime);

                logger.info("运单 {} 已到达终点，自动更新为已结束，距离 {} 米", delivery.getDeliveryNumber(), Math.round(distance));
            }
        } catch (NumberFormatException ex) {
            logger.warn("运单 {} 终点经纬度格式错误: lat={}, lon={}",
                delivery.getDeliveryNumber(), delivery.getEndLat(), delivery.getEndLon());
        }
    }

    private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        final double R = 6378137D;
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
                Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c;
    }
}

