package com.gci.boot.modules.gis.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gci.boot.annotation.Query.handle.QueryGenerator;
import com.gci.boot.common.util.*;
import com.gci.boot.enums.VehicleGpsType;
import com.gci.boot.exception.GciBootException;
import com.gci.boot.modules.basedata.entity.TbVehicle;
import com.gci.boot.modules.basedata.service.ITbVehicleService;
import com.gci.boot.modules.gis.entity.GisGpsAnalysis;
import com.gci.boot.modules.gis.entity.TbGisChargeLog;
import com.gci.boot.modules.gis.entity.TbGisGps;
import com.gci.boot.modules.gis.mapper.TbGisGpsMapper;
import com.gci.boot.modules.gis.query.GpsHistoryQuery;
import com.gci.boot.modules.gis.service.ITbGisChargeLogService;
import com.gci.boot.modules.gis.service.ITbGisGpsService;
import com.gci.boot.modules.gis.vo.GisHistoryVo;
import com.gci.boot.modules.gis.vo.PointListDTO;
import com.gci.boot.modules.gis.vo.TimeSlotDTO;
import com.gci.boot.modules.gis.vo.WorkLineVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 2464
 * @since 2023-09-01
 */
@Service
@Slf4j
public class TbGisGpsServiceImpl extends ServiceImpl<TbGisGpsMapper, TbGisGps> implements ITbGisGpsService {

    @Autowired
    private ITbVehicleService vehicleService;
    @Autowired
    private ITbGisChargeLogService chargeLogService;
    @Value("${gps.timeCut}")
    private Long timeCut;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public List<GisHistoryVo> getGpsHistory(GpsHistoryQuery vo) {
        List<Date> time = vo.getTime();
        if (DateUtil.between(time.get(0), time.get(1), DateUnit.MS, false) < 0) {
            throw new GciBootException("开始时间不能大于结束时间！");
        }
/*        if (DateUtil.between(time.get(0), time.get(1), DateUnit.DAY, false) > 2) {
            throw new GciBootException("开始时间和结束时间不能超过两天！");
        }*/

        //返回给前端的list
        List<GisHistoryVo> gisHistoryVos = new ArrayList<>();

        TbVehicle one = vehicleService.getOne(new LambdaQueryWrapper<TbVehicle>().eq(TbVehicle::getPlateNumber, vo.getPlateNumber()));
        Integer batteryCapacity = null;
        if (ObjectUtil.isEmpty(one)) {
            return gisHistoryVos;
        }
        if (ObjectUtil.isNotEmpty(one.getBatteryCapacity())) {
            batteryCapacity = one.getBatteryCapacity();
        }


        //是否三电
        boolean isEic = false;
        if (Convert.toBool(redisTemplate.opsForValue().get(RedisKey.PROJECT_GIS_SOURCE + one.getProjectId()),false)) {
            isEic = true;
        }
        LambdaQueryWrapper<TbGisGps> lambdaQueryWrapper = QueryGenerator.initQueryWrapper(TbGisGps.class, vo);
        //在sql查询时直接排除异常数据
        lambdaQueryWrapper.eq(TbGisGps::getIsValid,1)
                .isNotNull(TbGisGps::getLat)
                .isNotNull(TbGisGps::getLon)
                .ne(TbGisGps::getLon,0)
                .ne(TbGisGps::getLat,0)
                .eq(TbGisGps::getRecordSource,isEic ? 2 : 1);
        List<TbGisGps> list = list(lambdaQueryWrapper);
        if (ObjectUtil.isEmpty(list) && !isEic) {
            isEic = true;
            lambdaQueryWrapper.clear();
            lambdaQueryWrapper = QueryGenerator.initQueryWrapper(TbGisGps.class, vo);
            lambdaQueryWrapper.eq(TbGisGps::getIsValid,1)
                    .isNotNull(TbGisGps::getLat)
                    .isNotNull(TbGisGps::getLon)
                    .ne(TbGisGps::getLon,0)
                    .ne(TbGisGps::getLat,0)
                    .eq(TbGisGps::getRecordSource, 2);
            list = list(lambdaQueryWrapper);
        }

        boolean finalIsEic = isEic;
        list = list.stream().filter(g->{
            if (ObjectUtil.isAllNotEmpty(g.getAcc(),g.getSpeed())) {
                //如果有只需要三电数据
                if (finalIsEic) {
                    if (g.getTotalMileage() == null || g.getTotalMileage().compareTo(BigDecimal.ZERO) <= 0) {
                        return false;
                    }
                    return g.getRecordSource().equals(2);
                }
                return true;
            } else {
                return false;
            }
        }).collect(Collectors.toList());



        if (ObjectUtil.isEmpty(list)) {
            return gisHistoryVos;
        }


        //行驶数据
        drivingDataV2(batteryCapacity, list,gisHistoryVos,isEic);
        //充电数据 只查询结束时间在查询条件时间条件区间内
        powerData(vo,gisHistoryVos);

        //再排序两遍
        if (ObjectUtil.isNotEmpty(gisHistoryVos)) {
            for (GisHistoryVo gisHistoryVo : gisHistoryVos) {
                List<TimeSlotDTO> timeSlot = gisHistoryVo.getTimeSlot();
                timeSlot.sort(Comparator.comparing(TimeSlotDTO::getSlotTime).reversed());
            }
        }
        gisHistoryVos.sort(Comparator.comparing(GisHistoryVo::getDayTime).reversed());
        return gisHistoryVos;
    }

    @Override
    public List<TbGisGps> getGpsListByVehicleAndTime(TbVehicle vehicle, Date startTime, Date endTime,int recordSource) {
        //构建gps数据记录查询
        LambdaQueryWrapper<TbGisGps> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TbGisGps::getPlateNumber, vehicle.getPlateNumber());
        queryWrapper.between(TbGisGps::getGpsTime, startTime, endTime);
        // TODO: 2023/10/23 只查三电来源且有正确工作状态的数据
        queryWrapper.eq(TbGisGps::getRecordSource, recordSource);
//        queryWrapper.and(wrapper -> {
//            wrapper.eq(TbGisGps::getIsWork, 1).or().eq(TbGisGps::getIsWork, 0);
//        });
        queryWrapper.eq(TbGisGps::getIsValid, 1);
        queryWrapper.orderByAsc(TbGisGps::getGpsTime);
        List<TbGisGps> gpsList = this.list(queryWrapper);
        return gpsList;
    }

    private void powerData(GpsHistoryQuery query, List<GisHistoryVo> gisHistoryVos) {
        List<Date> time = query.getTime();
        List<TbGisChargeLog> tbGisChargeLogs = chargeLogService.list(new LambdaQueryWrapper<TbGisChargeLog>().eq(TbGisChargeLog::getNumberPlate,query.getPlateNumber()).between(TbGisChargeLog::getEndTime, time.get(0), time.get(1)));
        if (ObjectUtil.isNotEmpty(tbGisChargeLogs)) {
            for (TbGisChargeLog tbGisChargeLog : tbGisChargeLogs) {
                Date startTime = tbGisChargeLog.getStartTime();
                Date endTime = tbGisChargeLog.getEndTime();

                String day = DateUtil.format(startTime, "yyyy-MM-dd");
                DateTime dayTime = DateUtil.beginOfDay(startTime);
                String startFormat = DateUtil.format(startTime, "HH:mm");
                String endFormat = DateUtil.format(endTime, "HH:mm");
                String formatBetween = DateUtil.formatBetween(startTime, endTime, BetweenFormatter.Level.MINUTE);
                boolean sameDay = DateUtil.isSameDay(startTime, endTime);


                TimeSlotDTO timeSlotDTO = new TimeSlotDTO();
                //充电
                timeSlotDTO.setType("power");
                timeSlotDTO.setSlotTime(startTime);
                timeSlotDTO.setTimeCount(formatBetween);
                timeSlotDTO.setIsOvernight(sameDay ? 0 : 1);
                timeSlotDTO.setTimeRange(startFormat + " - " + endFormat);
                timeSlotDTO.setChargedEnergy(tbGisChargeLog.getChargingEnergy());
                timeSlotDTO.setChargedEfficiency(tbGisChargeLog.getChargingEfficiency());

                ArrayList<PointListDTO> pointListDTOS = new ArrayList<>();
                PointListDTO pointListDTO = new PointListDTO();
                ArrayList<Double> doubles = new ArrayList<>();
                doubles.add(tbGisChargeLog.getGdLon().doubleValue());
                doubles.add(tbGisChargeLog.getGdLat().doubleValue());
                pointListDTO.setLocation(doubles);
                pointListDTOS.add(pointListDTO);
                timeSlotDTO.setPointList(pointListDTOS);
                GisHistoryVo gisHistoryVo = gisHistoryVos.stream().filter(gisHistoryVo1 -> gisHistoryVo1.getDay().equals(day)).findFirst().orElse(new GisHistoryVo());
                if (BeanUtil.isEmpty(gisHistoryVo)) {
                    gisHistoryVo.setDay(day);
                    gisHistoryVo.setDayTime(dayTime);
                    ArrayList<TimeSlotDTO> timeSlotDTOS = new ArrayList<>();
                    timeSlotDTOS.add(timeSlotDTO);
                    gisHistoryVo.setTimeSlot(timeSlotDTOS);
                    gisHistoryVos.add(gisHistoryVo);
                } else {
                    gisHistoryVo.getTimeSlot().add(timeSlotDTO);
                }
            }
        }
    }

    private void drivingDataV2(Integer batteryCapacity, List<TbGisGps> list, List<GisHistoryVo> gisHistoryVos, boolean isEic) {
        ArrayList<TimeSlotDTO> dtoArrayList = new ArrayList<>();
        TimeSlotDTO timeSlotDTO = null;
        for (TbGisGps gps : list) {
            if (ObjectUtil.isEmpty(gps.getIsWork())) {
                gps.setIsWork(0);
            }
            // 根据规则判断记录类型
            int recordType;
            if (isDrivingWork(gps)) {
                recordType = VehicleGpsType.Driving_Work.getValue();
            } else if (isDrivingNotWork(gps)) {
                recordType = VehicleGpsType.Driving_Not_Work.getValue();
            } else if (isStoppedWork(gps)) {
                recordType = VehicleGpsType.Stopped_Work.getValue();
            } else if (isStoppedNotWork(gps)){
                recordType = VehicleGpsType.Stopped_NOT_Work.getValue();
            }else{
                continue;
            }
            if (timeSlotDTO == null || recordType != timeSlotDTO.getRecordType()) {
//                addTimeDTO(beforeMerge,timeSlotDTO);
                // 如果当前分段为空或者记录类型不同，创建新的分段
                timeSlotDTO = createTimeDTO(gps,recordType,batteryCapacity,isEic);
                dtoArrayList.add(timeSlotDTO);
            } else {
                // 否则，将当前GPS数据添加到当前分段
                if (DateUtil.between(timeSlotDTO.getEndTime(),gps.getGpsTime(),DateUnit.MINUTE,false) < 20) {
                    updateTimeDTO(timeSlotDTO, gps,batteryCapacity,isEic,true);
                } else {
                    timeSlotDTO = createTimeDTO(gps,recordType,batteryCapacity,isEic);
                    dtoArrayList.add(timeSlotDTO);
                }
            }
        }
        if (!dtoArrayList.isEmpty()) {
            mergeGisHistoryVo(gisHistoryVos,dtoArrayList,batteryCapacity,isEic);
        }
    }

    private void mergeGisHistoryVo(List<GisHistoryVo> gisHistoryVos, ArrayList<TimeSlotDTO> beforeMerge, Integer batteryCapacity, boolean isEic) {
        List<TimeSlotDTO> afterMerge = new ArrayList<>();
        TimeSlotDTO pre = null;
        for (TimeSlotDTO timeSlotDTO : beforeMerge) {
/*            if (timeSlotDTO.getRecordType() == 3 || timeSlotDTO.getRecordType() == 4) {
                continue;
            }*/
            if (timeSlotDTO.getMileage().compareTo(BigDecimal.ZERO) <= 0) {
                continue;
            }
            if (pre == null) {
                pre = timeSlotDTO;
            } else {
                if (areMergable(pre,timeSlotDTO,isEic)) {
                    mergeData(pre, timeSlotDTO,batteryCapacity,isEic);
                } else {
                    afterMerge.add(timeSlotDTO);
                    pre = timeSlotDTO;
                }
            }
        }
/*        if (pre != null) {
            afterMerge.add(pre);
        }*/
        for (TimeSlotDTO timeSlotDTO : afterMerge) {
            addTimeDTO(gisHistoryVos,timeSlotDTO);
        }
    }

    private void mergeData(TimeSlotDTO previousData, TimeSlotDTO currentData, Integer batteryCapacity, boolean isEic) {
        previousData.setEndTime(currentData.getEndTime());
        if (!((previousData.getRecordType() == 4 && currentData.getRecordType() ==4) ||
                (previousData.getRecordType() == 3 && currentData.getRecordType() == 3))) {
            previousData.setLogMerge(Boolean.TRUE);
        }
        Date startTime = previousData.getSlotTime();
        Date endTime = previousData.getEndTime();
        String startFormat = DateUtil.format(startTime, "HH:mm");
        String endFormat = DateUtil.format(endTime, "HH:mm");
        String formatBetween = DateUtil.formatBetween(startTime, endTime, BetweenFormatter.Level.MINUTE);
        if (DateUtil.between(startTime, endTime, DateUnit.SECOND) < 60) {
            formatBetween = "1分";
        }
        boolean sameDay = DateUtil.isSameDay(startTime, endTime);

        previousData.setTimeRange(startFormat+ "-" +endFormat);
        previousData.setTimeCount(formatBetween);
        previousData.setIsOvernight(sameDay ? 0 : 1);

        if(isEic){
            previousData.setEndMile(currentData.getEndMile());
            BigDecimal totalMileage = previousData.getEndMile().subtract(previousData.getStartMile());
            previousData.setMileage(totalMileage);
            if(ObjectUtil.isAllNotEmpty(previousData.getEndGisGpsStateOfCharge(),currentData.getEndGisGpsStateOfCharge())){
                if (previousData.getStartGisGpsStateOfCharge() > 0) {
                    previousData.setEndGisGpsStateOfCharge(currentData.getEndGisGpsStateOfCharge());
                    Integer startGisGpsStateOfCharge = previousData.getStartGisGpsStateOfCharge();
                    Integer endGisGpsStateOfCharge = previousData.getEndGisGpsStateOfCharge();

                    BigDecimal sub = NumberUtil.sub(startGisGpsStateOfCharge, endGisGpsStateOfCharge);
                    BigDecimal div = NumberUtil.div(sub, 100);
                    BigDecimal mul = NumberUtil.mul(div, batteryCapacity).setScale(2,BigDecimal.ROUND_HALF_UP);

                    previousData.setPowerConsumption(mul);
                }
            }
        }else{
            previousData.setMileage(previousData.getMileage().add(currentData.getMileage()));
        }
        BigDecimal mileage = previousData.getMileage();
        double between = betweenHour(startTime, endTime);
        if (between > 0) {
            previousData.setAverageSpeed(NumberUtil.div(mileage,between,2).doubleValue());
        }
        List<PointListDTO> pointList = previousData.getPointList();
        pointList.addAll(currentData.getPointList());
        previousData.setPointList(pointList);
    }

    private boolean areMergable(TimeSlotDTO previousData, TimeSlotDTO currentData, boolean isEic) {
        if(isEic){
            return (calculateTimeDifferenceInMinutes(previousData.getEndTime(), currentData.getSlotTime()) <= 20
            && sameType(previousData,currentData));
        }else{
            return (calculateTimeDifferenceInMinutes(previousData.getEndTime(), currentData.getSlotTime()) <= 20
            && sameType(previousData,currentData));
        }
    }

    /**
     * 是否同类型段
     * @param previousData
     * @param currentData
     * @return
     */
    private boolean sameType(TimeSlotDTO previousData, TimeSlotDTO currentData) {
        if (previousData.getRecordType() == 1 || previousData.getRecordType() == 4) {
            return currentData.getRecordType() == 1 || currentData.getRecordType() == 4;
        } else if (previousData.getRecordType() == 2 || previousData.getRecordType() ==3) {
            return currentData.getRecordType() == 2 || currentData.getRecordType() == 3;
        }
        return false;
    }

    private void addTimeDTO(List<GisHistoryVo> gisHistoryVos, TimeSlotDTO timeSlotDTO) {
        if (ObjectUtil.isNotEmpty(timeSlotDTO)) {
            if (timeSlotDTO.getMileage().compareTo(BigDecimal.ZERO) == 0) {
                return;
            }
            if ((!timeSlotDTO.getLogMerge()) && (timeSlotDTO.getRecordType() == 3 || timeSlotDTO.getRecordType() == 4)) {
                return;
            }
            String day = DateUtil.format(timeSlotDTO.getSlotTime(), "yyyy-MM-dd");
            DateTime dayTime = DateUtil.beginOfDay(timeSlotDTO.getSlotTime());
            GisHistoryVo gisHistoryVo = gisHistoryVos.stream().filter(gisHistoryVo1 -> gisHistoryVo1.getDay().equals(day)).findFirst().orElse(new GisHistoryVo());
            if (BeanUtil.isEmpty(gisHistoryVo)) {
                gisHistoryVo.setDay(day);
                gisHistoryVo.setDayTime(dayTime);
                ArrayList<TimeSlotDTO> timeSlotDTOS = new ArrayList<>();
                timeSlotDTOS.add(timeSlotDTO);
                gisHistoryVo.setTimeSlot(timeSlotDTOS);
                gisHistoryVos.add(gisHistoryVo);
            } else {
                List<TimeSlotDTO> timeSlot = gisHistoryVo.getTimeSlot();
                TimeSlotDTO timeSlotDTO1 = timeSlot.get(timeSlot.size() - 1);
                List<PointListDTO> pointList = timeSlotDTO1.getPointList();
                PointListDTO pointListDTO = pointList.get(pointList.size() - 1);
                timeSlotDTO.getPointList().add(0,pointListDTO);
                timeSlot.add(timeSlotDTO);
            }
        }
    }

    private TimeSlotDTO createTimeDTO(TbGisGps gps, int recordType, Integer batteryCapacity, boolean isEic) {
        TimeSlotDTO timeSlotDTO = new TimeSlotDTO   ();
        if (recordType == 1 || recordType == 4) {
            timeSlotDTO.setType("driving");
        } else {
            timeSlotDTO.setType("working");
        }
        timeSlotDTO.setSlotTime(gps.getGpsTime());
        timeSlotDTO.setEndTime(gps.getGpsTime());
        timeSlotDTO.setRecordType(recordType);
        timeSlotDTO.setLogMerge(Boolean.FALSE);
        ArrayList<PointListDTO> pointListDTOS = new ArrayList<>();
        pointListDTOS.add(buildPointListDTO(gps));
        timeSlotDTO.setPointList(pointListDTOS);
        if (isEic) {
            if (ObjectUtil.isNotEmpty(gps.getStateOfCharge())) {
                timeSlotDTO.setStartGisGpsStateOfCharge(gps.getStateOfCharge());
                timeSlotDTO.setEndGisGpsStateOfCharge(gps.getStateOfCharge());
            }
            timeSlotDTO.setStartMile(gps.getTotalMileage());
        } else {
            timeSlotDTO.setMileage(BigDecimal.ZERO);
            timeSlotDTO.setLon(gps.getLon());
            timeSlotDTO.setLat(gps.getLat());
        }
        updateTimeDTO(timeSlotDTO, gps,batteryCapacity,isEic,false);
        return timeSlotDTO;
    }

    private void updateTimeDTO(TimeSlotDTO timeSlotDTO, TbGisGps gps, Integer batteryCapacity, boolean isEic, boolean needAdd) {
        timeSlotDTO.setEndTime(gps.getGpsTime());

        Date startTime = timeSlotDTO.getSlotTime();
        Date endTime = timeSlotDTO.getEndTime();
        String startFormat = DateUtil.format(startTime, "HH:mm");
        String endFormat = DateUtil.format(endTime, "HH:mm");
        String formatBetween = DateUtil.formatBetween(startTime, endTime, BetweenFormatter.Level.MINUTE);
        boolean sameDay = DateUtil.isSameDay(startTime, endTime);

        timeSlotDTO.setTimeRange(startFormat+ "-" +endFormat);
        timeSlotDTO.setTimeCount(formatBetween);
        timeSlotDTO.setIsOvernight(sameDay ? 0 : 1);

        if (isEic) {
            if (ObjectUtil.isAllNotEmpty(gps.getStateOfCharge(),batteryCapacity)) {
                timeSlotDTO.setEndGisGpsStateOfCharge(gps.getStateOfCharge());
                Integer startGisGpsStateOfCharge = timeSlotDTO.getStartGisGpsStateOfCharge();
                Integer endGisGpsStateOfCharge = timeSlotDTO.getEndGisGpsStateOfCharge();

                if (startGisGpsStateOfCharge > 0) {
                    BigDecimal sub = NumberUtil.sub(startGisGpsStateOfCharge, endGisGpsStateOfCharge);
                    BigDecimal div = NumberUtil.div(sub, 100);
                    BigDecimal mul = NumberUtil.mul(div, batteryCapacity).setScale(2,BigDecimal.ROUND_HALF_UP);

                    timeSlotDTO.setPowerConsumption(mul);
                }

            }
            timeSlotDTO.setEndMile(gps.getTotalMileage());
            timeSlotDTO.setMileage(timeSlotDTO.getEndMile().subtract(timeSlotDTO.getStartMile()));
        } else {
            long duration = DateUtil.betweenMs(timeSlotDTO.getEndTime(),timeSlotDTO.getSlotTime())/1000;
            double distance =  CalcTool.getDistance(timeSlotDTO.getLon().doubleValue(),timeSlotDTO.getLat().doubleValue(),gps.getLon().doubleValue(),gps.getLat().doubleValue());
            if(duration>0 && distance /duration < 0.055 ){
                timeSlotDTO.setMileage(timeSlotDTO.getMileage().add(new BigDecimal(distance)).setScale(2,BigDecimal.ROUND_HALF_UP));
                timeSlotDTO.setLon(gps.getLon());
                timeSlotDTO.setLat(gps.getLat());
            }
        }


        BigDecimal mileage = timeSlotDTO.getMileage();
        double between = betweenHour(startTime, endTime);
        if (between > 0) {
            timeSlotDTO.setAverageSpeed(NumberUtil.div(mileage,between,2).doubleValue());
        }
        if (needAdd) {
            List<PointListDTO> pointList = timeSlotDTO.getPointList();
            pointList.add(buildPointListDTO(gps));
//            timeSlotDTO.setPointList(pointList);
        }
    }

    private void drivingData(Integer batteryCapacity, List<TbGisGps> list, List<GisHistoryVo> gisHistoryVos) {
        List<List<TbGisGps>> lists = groupByStatusAndTime(list);
        if (ObjectUtil.isNotEmpty(lists)) {
            for (List<TbGisGps> tbGisGps : lists) {
                if (ObjectUtil.isEmpty(tbGisGps)) {
                    continue;
                }
                TbGisGps startGisGps = tbGisGps.get(0);
                TbGisGps endGisGps = tbGisGps.get(tbGisGps.size() - 1);
                Date startGisGpsGpsTime = startGisGps.getGpsTime();
                Date endGisGpsGpsTime = endGisGps.getGpsTime();
                BigDecimal startGisGpsTotalMileage = startGisGps.getTotalMileage();
                BigDecimal endGisGpsTotalMileage = endGisGps.getTotalMileage();
                Integer startGisGpsStateOfCharge = startGisGps.getStateOfCharge();
                Integer endGisGpsStateOfCharge = endGisGps.getStateOfCharge();

                String day = DateUtil.format(startGisGpsGpsTime, "yyyy-MM-dd");
                DateTime dayTime = DateUtil.beginOfDay(startGisGpsGpsTime);
                String startFormat = DateUtil.format(startGisGpsGpsTime, "HH:mm");
                String endFormat = DateUtil.format(endGisGpsGpsTime, "HH:mm");
                String formatBetween = DateUtil.formatBetween(startGisGpsGpsTime, endGisGpsGpsTime, BetweenFormatter.Level.MINUTE);
                boolean sameDay = DateUtil.isSameDay(startGisGpsGpsTime, endGisGpsGpsTime);

                TimeSlotDTO timeSlotDTO = new TimeSlotDTO();
                //行驶
                timeSlotDTO.setType("driving");
                timeSlotDTO.setSlotTime(startGisGpsGpsTime);
                timeSlotDTO.setTimeCount(formatBetween);
                timeSlotDTO.setIsOvernight(sameDay ? 0 : 1);
                timeSlotDTO.setTimeRange(startFormat + " - " + endFormat);

                if (ObjectUtil.isAllNotEmpty(startGisGpsTotalMileage, endGisGpsTotalMileage)) {
                    if (startGisGpsTotalMileage.compareTo(endGisGpsTotalMileage) == 0) {
                        continue;
                    }
                    timeSlotDTO.setMileage(NumberUtil.sub(endGisGpsTotalMileage, startGisGpsTotalMileage));
                    if (ObjectUtil.isAllNotEmpty(startGisGpsGpsTime,endGisGpsGpsTime)) {
                        BigDecimal mileage = timeSlotDTO.getMileage();
                        double between = betweenHour(startGisGpsGpsTime, endGisGpsGpsTime);
                        if (between > 0) {
                            timeSlotDTO.setAverageSpeed(NumberUtil.div(mileage,between,2).doubleValue());
                        }
                    }
                }
                if (ObjectUtil.isAllNotEmpty(startGisGpsStateOfCharge,endGisGpsStateOfCharge, batteryCapacity)) {

                    BigDecimal sub = NumberUtil.sub(startGisGpsStateOfCharge, endGisGpsStateOfCharge);
                    BigDecimal div = NumberUtil.div(sub, 100);
                    BigDecimal mul = NumberUtil.mul(div, batteryCapacity).setScale(2,BigDecimal.ROUND_HALF_UP);

                    timeSlotDTO.setPowerConsumption(mul);

                }
                ArrayList<PointListDTO> pointListDTOS = new ArrayList<>();
                for (TbGisGps tbGisGp : tbGisGps) {
                    PointListDTO pointListDTO = buildPointListDTO(tbGisGp);
                    pointListDTOS.add(pointListDTO);
                }
                timeSlotDTO.setPointList(pointListDTOS);

                GisHistoryVo gisHistoryVo = gisHistoryVos.stream().filter(gisHistoryVo1 -> gisHistoryVo1.getDay().equals(day)).findFirst().orElse(new GisHistoryVo());
                if (BeanUtil.isEmpty(gisHistoryVo)) {
                    gisHistoryVo.setDay(day);
                    gisHistoryVo.setDayTime(dayTime);
                    ArrayList<TimeSlotDTO> timeSlotDTOS = new ArrayList<>();
                    timeSlotDTOS.add(timeSlotDTO);
                    gisHistoryVo.setTimeSlot(timeSlotDTOS);
                    gisHistoryVos.add(gisHistoryVo);
                } else {
                    gisHistoryVo.getTimeSlot().add(timeSlotDTO);
                }
            }
        }
    }

    private PointListDTO buildPointListDTO(TbGisGps tbGisGp) {
        PointListDTO pointListDTO = new PointListDTO();

        BigDecimal lat = tbGisGp.getLat();
        BigDecimal lon = tbGisGp.getLon();
        LatLonPoint latLonPoint = CoordinateConverterUtil.toWGSPoint(lat.doubleValue(), lon.doubleValue());
        ArrayList<Double> doubles = new ArrayList<>();
        doubles.add(latLonPoint.getLongitude());
        doubles.add(latLonPoint.getLatitude());

        pointListDTO.setLocation(doubles);
        pointListDTO.setStateOfCharge(tbGisGp.getStateOfCharge());
        pointListDTO.setSpeed(tbGisGp.getSpeed());
        pointListDTO.setTotalMileage(tbGisGp.getTotalMileage());
        pointListDTO.setTotalVoltage(tbGisGp.getTotalVoltage());
        pointListDTO.setGpsTime(DateUtil.formatDateTime(tbGisGp.getGpsTime()));
        pointListDTO.setAcc(tbGisGp.getAcc());
        pointListDTO.setDcStatus(tbGisGp.getDcStatus());
        pointListDTO.setOperatingMode(tbGisGp.getOperatingMode());
        pointListDTO.setIsWork(tbGisGp.getIsWork());
        return pointListDTO;
    }

    private List<List<TbGisGps>> groupByStatusAndTime(List<TbGisGps> list) {
        // 创建一个空的结果列表
        List<List<TbGisGps>> result = new ArrayList<>();
        // 如果输入列表为空，直接返回结果
        if (list == null || list.isEmpty()) {
            return result;
        }
        // 创建一个临时的子列表，用于存放符合条件的元素
        List<TbGisGps> subList = new ArrayList<>();
        // 创建速度缓存的
        ArrayList<TbGisGps> timeList = new ArrayList<>();
        // 遍历输入列表中的每个元素
        for (int i = 0; i < list.size(); i++) {
            // 获取当前元素
            TbGisGps current = list.get(i);
            // 获取当前状态 1-点火 0-熄火
            int currentStatus = current.getCurrentStatus();
            current.setCerStatus(currentStatus);
            // 点火状态时
            if (currentStatus == 1) {
                //临时列表为空或者上一个也是点火状态
                if (subList.isEmpty() || subList.get(subList.size() - 1).getCerStatus() == currentStatus) {
                    //数据可能有缺失
                    if (!subList.isEmpty()) {
                        TbGisGps tbGisGps = subList.get(subList.size() - 1);
                        //如果超过了间隔就截断
                        long between = DateUtil.between(tbGisGps.getGpsTime(), current.getGpsTime(), DateUnit.MINUTE, false);
                        if (between > timeCut) {
                            timeList = new ArrayList<>();
                            if (!subList.isEmpty()) {
                                result.add(subList);
                                subList = new ArrayList<>();
                            }
                        }
                    }
                    //没速度先缓存到时间缓存列表里面
                    if (ObjectUtil.isEmpty(current.getSpeed()) || current.getSpeed().compareTo(BigDecimal.ZERO) == 0) {
                        timeList.add(current);
                    } else {
                        //有速度判断一下是否抛弃数据
                        if (!timeList.isEmpty()) {
                            TbGisGps last = timeList.get(0);
                            //当前的速度不为零时 超过了设置时间
                            long between = DateUtil.between(last.getGpsTime(), current.getGpsTime(), DateUnit.MINUTE, false);
                            if (between > timeCut) {
                                //清空速度缓存
                                timeList = new ArrayList<>();
                                //结束临时列表
                                if (!subList.isEmpty()) {
                                    result.add(subList);
                                    subList = new ArrayList<>();
                                }
                            } else {
                                //新数据上来了 并且没有超过设置时间
                                //将时间缓存加入临时列表
                                subList.addAll(timeList);
                                //清空时间缓存
                                timeList = new ArrayList<>();
                            }
                        }
                        subList.add(current);
                    }
                } else {
                    log.warn("出现了意料之外的情况，什么情况会到这里？list数据为:{}", JsonUtil.object2Json(list));
                }
            } else if (currentStatus == 0) {
                //熄火状态时
                //临时列表不为空
                if (!subList.isEmpty()) {
                    //时间缓存不为空
                    if (!timeList.isEmpty()) {
//                        subList.addAll(timeList);
                        //不要时间缓存了
                        timeList = new ArrayList<>();
                    }
                    //加入结束点
//                    subList.add(current);
                    result.add(subList);
                    //新建临时列表
                    subList = new ArrayList<>();
                }
            }
        }
        // 遍历结束后，判断临时列表是否为空
        if (!subList.isEmpty()) {
            result.add(subList);
        }
        // 返回结果列表
        return result;
    }

    private List<List<TbGisGps>> groupByStatusAndTimeV2(List<TbGisGps> list) {
        // 创建一个空的结果列表
        List<List<TbGisGps>> result = new ArrayList<>();
        // 如果输入列表为空，直接返回结果
        if (list == null || list.isEmpty()) {
            return result;
        }
        // 创建一个临时的子列表，用于存放符合条件的元素
        List<TbGisGps> subList = new ArrayList<>();
        // 创建速度为0或无效缓存的
        ArrayList<TbGisGps> speedList = new ArrayList<>();
        for (TbGisGps current : list) {
            //1-点火 0-熄火 其他的为不正常数据
            int currentStatus = current.getCurrentStatus();
            current.setCerStatus(currentStatus);
            if (currentStatus == 1) {
                //点火状态时
                Integer chargeStatus = current.getChargeStatus();
                //没有充电的时候
                if (chargeStatus == null || chargeStatus == 3 || chargeStatus == 4) {
                    if (subList.isEmpty()) {
                        //临时列表为空是一定要加上的
                        subList.add(current);
                        //速度缓存可能不为空都有要清空掉
                        speedList = new ArrayList<>();
                    } else {
                        //不为空需要判断一下是否能够加上数据
                        if (ObjectUtil.isNotEmpty(current.getSpeed()) && current.getSpeed().compareTo(BigDecimal.ZERO) > 0) {
                            //有速度
                            //判断是否可以加入缓存
                            if (subList.isEmpty()) {
                                subList.add(current);
                                continue;
                            } else {
                                //和列表内的最后一条数据比较是否超过timeCut
                                //超过就不需要这条数据
                                TbGisGps lastSub = subList.get(subList.size() - 1);
                                long between = DateUtil.between(lastSub.getGpsTime(), current.getGpsTime(), DateUnit.MINUTE, false);
                                if (between > timeCut) {
                                    //超过说明需要结束临时列表 (速度缓存不需要加入 超过timeCut说明连最新一条数据都超过了，之前的数据肯定也是没速度的)
                                    result.add(subList);
                                    //清空 临时列表 速度缓存
                                    subList = new ArrayList<>();
                                    speedList= new ArrayList<>();
                                    continue;
                                } else {
                                    //没超过说明需要加入中间的速度数据
                                    if (!speedList.isEmpty()) {
                                        subList.addAll(speedList);
                                        //清空速度缓存
                                        speedList= new ArrayList<>();
                                    }
                                    subList.add(current);
                                    continue;
                                }
                            }
                        } else if (ObjectUtil.isEmpty(current.getSpeed()) || current.getSpeed().compareTo(BigDecimal.ZERO) <= 0){
                            //没速度
                            //直接加入缓存
                            speedList.add(current);
                            continue;
                        }
                    }
                } else if (chargeStatus == 1 || chargeStatus ==2) {
                    //在充电
                    //结束临时列表
                    if (!subList.isEmpty()) {
                        result.add(subList);
                        subList = new ArrayList<>();
                    }
                    if (!speedList.isEmpty()) {
                        speedList = new ArrayList<>();
                    }
                } else {
                    //点火时 充电状态出现了异常数据 判断一下是否可以加入数据内
                    if (subList.isEmpty()) {
                        continue;
                    } else {
                        //临时列表不为空的时候才考虑加不加进去
                        TbGisGps lastSub = subList.get(subList.size() - 1);
                        long between = DateUtil.between(lastSub.getGpsTime(), current.getGpsTime(), DateUnit.MINUTE, false);
                        if (between > timeCut) {
                            //如果超过timeCut
                            //这条数据就不要了
                            continue;
                        } else {
                            //未超过的话判断一下关键数据是否正常
                            if (ObjectUtil.isEmpty(current.getSpeed()) || current.getSpeed().compareTo(BigDecimal.ZERO) < 0) {
                                //速度不正常
                                //抛弃
                                continue;
                            }
//                            if (ObjectUtil.isEmpty(current.get))
                            //数据正常就加入到临时列表
                            if (!speedList.isEmpty()) {
                                //速度缓存也加入
                                subList.addAll(speedList);
                                //清空速度缓存
                                speedList = new ArrayList<>();
                            }
                        }
                    }
                }
            } else if (currentStatus == 0) {
                //熄火时
                //speed就不要了
                speedList = new ArrayList<>();
                //结束缓存列表
                if (!subList.isEmpty()) {
                    result.add(subList);
                    subList = new ArrayList<>();
                }
            } else {
                //异常状态时
                Integer chargeStatus = current.getChargeStatus();
                //没有充电的时候
                if (chargeStatus == null || chargeStatus == 3 || chargeStatus == 4) {
                    if (subList.isEmpty()) {
                        //临时列表为空是一定要加上的
                        subList.add(current);
                        //速度缓存可能不为空都有要清空掉
                        speedList = new ArrayList<>();
                    } else {
                        //不为空需要判断一下是否能够加上数据
                        if (ObjectUtil.isNotEmpty(current.getSpeed()) && current.getSpeed().compareTo(BigDecimal.ZERO) > 0) {
                            //有速度
                            //判断是否可以加入缓存
                            if (subList.isEmpty()) {
                                subList.add(current);
                                continue;
                            } else {
                                //和列表内的最后一条数据比较是否超过timeCut
                                //超过就不需要这条数据
                                TbGisGps lastSub = subList.get(subList.size() - 1);
                                long between = DateUtil.between(lastSub.getGpsTime(), current.getGpsTime(), DateUnit.MINUTE, false);
                                if (between > timeCut) {
                                    //超过说明需要结束临时列表 (速度缓存不需要加入 超过timeCut说明连最新一条数据都超过了，之前的数据肯定也是没速度的)
                                    result.add(subList);
                                    //清空 临时列表 速度缓存
                                    subList = new ArrayList<>();
                                    speedList= new ArrayList<>();
                                    continue;
                                } else {
                                    //没超过说明需要加入中间的速度数据
                                    if (!speedList.isEmpty()) {
                                        subList.addAll(speedList);
                                        //清空速度缓存
                                        speedList= new ArrayList<>();
                                    }
                                    subList.add(current);
                                }
                            }
                        } else if (ObjectUtil.isEmpty(current.getSpeed()) || current.getSpeed().compareTo(BigDecimal.ZERO) <= 0){
                            //没速度
                            //直接加入缓存
                            speedList.add(current);
                            continue;
                        }
                    }
                } else if (chargeStatus == 1 || chargeStatus ==2) {
                    //在充电
                    //结束临时列表
                    if (!subList.isEmpty()) {
                        result.add(subList);
                        subList = new ArrayList<>();
                    }
                    if (!speedList.isEmpty()) {
                        speedList = new ArrayList<>();
                    }
                } else {
                    //异常时 充电状态出现了异常数据 判断一下是否可以加入数据内
                    if (subList.isEmpty()) {
                        continue;
                    } else {
                        //临时列表不为空的时候才考虑加不加进去
                        TbGisGps lastSub = subList.get(subList.size() - 1);
                        long between = DateUtil.between(lastSub.getGpsTime(), current.getGpsTime(), DateUnit.MINUTE, false);
                        if (between > timeCut) {
                            //如果超过timeCut
                            //这条数据就不要了
                            continue;
                        } else {
                            //未超过的话判断一下关键数据是否正常
                            if (ObjectUtil.isEmpty(current.getSpeed()) || current.getSpeed().compareTo(BigDecimal.ZERO) < 0) {
                                //速度不正常
                                //抛弃
                                continue;
                            }
//                            if (ObjectUtil.isEmpty(current.get))
                            //数据正常就加入到临时列表
                            if (!speedList.isEmpty()) {
                                //速度缓存也加入
                                subList.addAll(speedList);
                                //清空速度缓存
                                speedList = new ArrayList<>();
                            }
                        }
                    }
                }
            }
        }
        return result;
    }

    private List<List<TbGisGps>> groupByStatusAndTimeV3(List<TbGisGps> list) {
        // 创建一个空的结果列表
        List<List<TbGisGps>> result = new ArrayList<>();
        // 如果输入列表为空，直接返回结果
        if (list == null || list.isEmpty()) {
            return result;
        }
        // 创建一个临时的子列表，用于存放符合条件的元素
        List<TbGisGps> subList = new ArrayList<>();
        // 创建速度缓存的
        ArrayList<TbGisGps> timeList = new ArrayList<>();
        //全部缓存一波数据
        List<List<TbGisGps>> beforeResult = new ArrayList<>();
        // 遍历输入列表中的每个元素
        for (TbGisGps current : list) {
            // 获取当前状态 1-点火 0-熄火 ()
            int currentStatus = current.getAcc() % 2;
            current.setAcc(currentStatus);
            if (DateUtil.format(current.getGpsTime(),"yyyy-MM-dd HH:mm").equals("2023-09-25 21:59")) {
                System.out.println("1");
            }
            //排除了空值还是会有异常值数据 也排除
            if (currentStatus < 0) {
                continue;
            } else if (current.getSpeed().compareTo(BigDecimal.ZERO) < 0) {
                continue;
            }
            if (currentStatus == 1) {
                //点火
                //点火有速度
                if (current.getSpeed().compareTo(BigDecimal.ZERO) > 0) {
                    if (!timeList.isEmpty()) {
                        //时间缓存的加入全部缓存
                        beforeResult.add(timeList);
                        timeList = new ArrayList<>();
                    }
                    subList.add(current);
                } else {
                    //点火没速度
                    if (!subList.isEmpty()) {
                        //有速度缓存的加入全部缓存
                        beforeResult.add(subList);
                        subList = new ArrayList<>();
                    }
                    timeList.add(current);
                }
            } else {
                //熄火
                if (!timeList.isEmpty()) {
                    //时间缓存的加入全部缓存
                    beforeResult.add(timeList);
                    timeList = new ArrayList<>();
                }
                if (!subList.isEmpty()) {
                    //有速度缓存的加入全部缓存
                    beforeResult.add(subList);
                    subList = new ArrayList<>();
                }
            }
        }
        if (!timeList.isEmpty()) {
            //时间缓存的加入全部缓存
            beforeResult.add(timeList);
        }
        if (!subList.isEmpty()) {
            //有速度缓存的加入全部缓存
            beforeResult.add(subList);
        }
        beforeResult.sort(Comparator.comparing(o -> o.get(0).getGpsTime()));
        // 返回结果列表
        for (List<TbGisGps> tbGisGps : beforeResult) {
            TbGisGps beforeFirst = tbGisGps.get(0);
            if (beforeFirst.getAcc() == 1 && beforeFirst.getSpeed().compareTo(BigDecimal.ZERO) > 0) {
                result.add(tbGisGps);
            } else {
                Date startTime = beforeFirst.getGpsTime();
                Date endTime = tbGisGps.get(tbGisGps.size() - 1).getGpsTime();
                if (beforeFirst.getAcc() == 1 && beforeFirst.getSpeed().compareTo(BigDecimal.ZERO) == 0){
                    if (!result.isEmpty()) {
                        List<TbGisGps> resultLast = result.get(result.size() - 1);
                        if (DateUtil.isOverlap(resultLast.get(0).getGpsTime(),resultLast.get(resultLast.size() - 1).getGpsTime(),startTime,startTime)) {
                            resultLast.addAll(tbGisGps);
                            resultLast.sort(Comparator.comparing(TbGisGps::getGpsTime));
                            result.set(result.size() - 1, resultLast);
                        } else {
                            result.add(tbGisGps);
                        }
                    }
                } else {
                    if (DateUtil.between(startTime, endTime, DateUnit.MINUTE, false) < timeCut) {
                        if (!result.isEmpty()) {
                            List<TbGisGps> resultLast = result.get(result.size() - 1);
                            resultLast.addAll(tbGisGps);
                            resultLast.sort(Comparator.comparing(TbGisGps::getGpsTime));
                            result.set(result.size() - 1, resultLast);
                        }
                    }
                }
            }
        }
        return result;
    }
    /**
     * 转换为小时
     */
    private double betweenHour(Date startTime,Date endTime) {
        return Convert.toDouble(DateUtil.betweenMs(startTime, endTime)) / 3600000;
    }

    /**
     * 行驶作业 点火且速度大于0
     *
     * @param gps
     * @return
     */
    private boolean isDrivingWork(TbGisGps gps) {
        return gps.getAcc()%2 == 1 && gps.getSpeed().compareTo(BigDecimal.ZERO) > 0 && (gps.getIsWork() == 1);
    }

    /**
     * 行驶未作业 点火、速度大于0、且未作业
     *
     * @param gps
     * @return
     */
    private boolean isDrivingNotWork(TbGisGps gps) {
        // 根据规则判断是否为“行驶未作业”
        return gps.getAcc()%2 == 1 && gps.getSpeed().compareTo(BigDecimal.ZERO) > 0 && (gps.getIsWork() == 0 || gps.getIsWork() == -1);
    }


    /**
     * 停靠作业 点火 速度小于等于0 且在作业  或者 熄火 且在作业
     *
     * @param gps
     * @return
     */
    private boolean isStoppedWork(TbGisGps gps) {
        return (gps.getAcc()%2 == 1 && gps.getSpeed().compareTo(BigDecimal.ZERO) <= 0 && (gps.getIsWork() == 1)) || (gps.getAcc() == 0 && (gps.getIsWork() == 1));
    }

    /**
     * 停靠未作业 点火 速度小于等于0 且未作业  或者 熄火 且未作业
     *
     * @param gps
     * @return
     */
    private boolean isStoppedNotWork(TbGisGps gps) {
        return (gps.getAcc()%2 == 1 && gps.getSpeed().compareTo(BigDecimal.ZERO) <= 0 && (gps.getIsWork() == 0 || gps.getIsWork() == -1)) || (gps.getAcc() == 0 && (gps.getIsWork() == 0 || gps.getIsWork() == -1));
    }

    private static long calculateTimeDifferenceInMinutes(Date startTime, Date endTime) {
        long timeDifference = endTime.getTime() - startTime.getTime();
        return timeDifference / (1000 * 60);
    }

}
