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

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.Week;
import cn.hutool.core.lang.Segment;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gci.boot.annotation.Query.handle.QueryGenerator;
import com.gci.boot.common.util.CalcTool;
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.gis.dto.EverydayMileCountDto;
import com.gci.boot.modules.gis.entity.GisGpsAnalysis;
import com.gci.boot.modules.gis.entity.TbGisGps;
import com.gci.boot.modules.gis.mapper.GisGpsAnalysisMapper;
import com.gci.boot.modules.gis.query.EverydayQuery;
import com.gci.boot.modules.gis.service.IGisGpsAnalysisService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gci.boot.modules.gis.service.ITbGisGpsService;
import com.gci.boot.modules.gis.vo.StatisGisVo;
import com.gci.boot.modules.gis.vo.VehicleWorkVO;
import com.gci.boot.modules.gis.vo.EverydayGisVo;
import com.gci.boot.modules.gis.vo.EverydayMileCountVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 车辆gps轨迹信息表 服务实现类
 * </p>
 *
 * @author 2464
 * @since 2023-09-01
 */
@Service
public class GisGpsAnalysisServiceImpl extends ServiceImpl<GisGpsAnalysisMapper, GisGpsAnalysis> implements IGisGpsAnalysisService {

    @Autowired
    private ITbGisGpsService tbGisGpsService;
    @Autowired
    private GisGpsAnalysisMapper gisGpsAnalysisMapper;

    @Override
    public void gpsDataAnalysisByVehicle(TbVehicle vehicle, Date startTime, Date endTime,boolean isEic) {

        int recordSource = isEic?2:1;
        List<TbGisGps> gpsList = tbGisGpsService.getGpsListByVehicleAndTime(vehicle,startTime,endTime,recordSource);

        if(isEic){
            //删除掉脏数据
            gpsList.removeIf(gps -> Objects.isNull(gps.getAcc()) || Objects.isNull(gps.getSpeed())
                    || Objects.isNull(gps.getTotalMileage()) || gps.getTotalMileage().compareTo(BigDecimal.ZERO)<=0);
        }else{
            //删除掉脏数据
            gpsList.removeIf(gps -> Objects.isNull(gps.getAcc()) || Objects.isNull(gps.getSpeed()));
        }
        // 初始化分段的数据
        List<GisGpsAnalysis> segments = new ArrayList<>();
        List<GisGpsAnalysis> result = new ArrayList<>();
        //如果是福龙马的，暂时特殊处理。
        if(vehicle.getVehicleBrand().equals("3")){
            segments =  genAnalysisFuLongMa(gpsList,vehicle,isEic);
            result = mergeGisData(segments,isEic);
        }else{
            segments = genAnalysisNormal(gpsList,vehicle,isEic);
            result = mergeGisData(segments,isEic);
        }
//        segments = genAnalysisNormal(gpsList,vehicle);
        // TODO: 2023/10/28 暂时屏蔽观察数据
//        segments.removeIf(s->Objects.isNull(s.getEndTime()));
        this.saveBatch(result, 500);
    }

    @Override
    public void gpsDataAnalysisByVehicleBy808(TbVehicle vehicle, Date startTime, Date endTime) {
        List<TbGisGps> gpsList = tbGisGpsService.getGpsListByVehicleAndTime(vehicle,startTime,endTime,1);
        //删除掉脏数据
        gpsList.removeIf(gps -> Objects.isNull(gps.getAcc()) || Objects.isNull(gps.getSpeed()));
        // 初始化分段的数据
        List<GisGpsAnalysis> segments = new ArrayList<>();
        //如果是福龙马的，暂时特殊处理。
        if(vehicle.getVehicleBrand().equals("3")){
            segments =  genAnalysisFuLongMa(gpsList,vehicle,false);
        }else{
            segments = genAnalysisNormal(gpsList,vehicle,false);
        }
//        segments = genAnalysisNormal(gpsList,vehicle);
        // TODO: 2023/10/28 暂时屏蔽观察数据
//        segments.removeIf(s->Objects.isNull(s.getEndTime()));
        List<GisGpsAnalysis> result = mergeGisData(segments,false);
        this.saveBatch(result, 500);
    }

    @Override
    public List<GisGpsAnalysis> gpsTime(TbVehicle vehicle, Date startTime, Date endTime) {
        List<TbGisGps> gpsList = tbGisGpsService.getGpsListByVehicleAndTime(vehicle,startTime,endTime,2);
        //删除掉脏数据
        gpsList.removeIf(gps -> Objects.isNull(gps.getAcc()) || Objects.isNull(gps.getSpeed())
                || Objects.isNull(gps.getTotalMileage())|| gps.getTotalMileage().compareTo(BigDecimal.ZERO)<=0);
        // 初始化分段的数据
        List<GisGpsAnalysis> segments = genAnalysis(gpsList,vehicle);
        segments.removeIf(s->s.getRecordType()==3);
        segments.sort(Comparator.comparing(GisGpsAnalysis::getStartTime));
        return segments;
    }

    private boolean isDriving(TbGisGps gps){
        return gps.getAcc()%2 == 1 && gps.getSpeed().compareTo(BigDecimal.ZERO) > 0;
    }

    private List<GisGpsAnalysis> genAnalysis(List<TbGisGps> gpsList,TbVehicle vehicle){
        List<GisGpsAnalysis> segments = new ArrayList<>();
        GisGpsAnalysis currentSegment = null;
        for (TbGisGps gps : gpsList) {
            // 根据规则判断记录类型
            int recordType;
            if(isDriving(gps)){
                recordType = VehicleGpsType.Driving_Work.getValue();
                //如果为空 或者 原来的为停靠，则需新增
                if(currentSegment == null || currentSegment.getRecordType() == VehicleGpsType.Stopped_Work.getValue()){
                    Optional<GisGpsAnalysis> option = segments.stream().filter(r->{
                        return r.getRecordType()==VehicleGpsType.Driving_Work.getValue();
                    }).reduce((first,second)->second);
                    if(option.isPresent()){
                        GisGpsAnalysis lastDriverObj = option.get();
                        long diff = gps.getGpsTime().getTime() - lastDriverObj.getEndTime().getTime();
                        if(diff < 20*60*1000){
                            updateSegment(lastDriverObj,vehicle, gps,true);
                        }else{
                            currentSegment = createSegment(gps, vehicle,recordType,true);
                            segments.add(currentSegment);
                        }
                    }else{
                        currentSegment = createSegment(gps, vehicle,recordType,true);
                        segments.add(currentSegment);
                    }
                }else{
                    updateSegment(currentSegment,vehicle, gps,true);
                }
            }else{
                recordType = VehicleGpsType.Stopped_Work.getValue();
                //如果原来的不为空 且 为行驶，且当前记录gps跟原来的差距小于20分钟;则追加，否则新曾一条
                if(currentSegment != null && currentSegment.getRecordType() == VehicleGpsType.Driving_Work.getValue()){
                    long diff = gps.getGpsTime().getTime() - currentSegment.getStartTime().getTime();
                    if(diff < 20*60*1000){
                        // 更新现有GPS分析数据段
                        currentSegment.setEndTime(gps.getGpsTime());
                        //得到时长(s)
                        Long duration = DateUtil.betweenMs(currentSegment.getEndTime(),currentSegment.getStartTime())/1000;
                        currentSegment.setDuration(duration);
                    }else{
                        currentSegment = createSegment(gps, vehicle,recordType,true);
                        segments.add(currentSegment);
                    }
                }else if(currentSegment != null && currentSegment.getRecordType() == VehicleGpsType.Stopped_Work.getValue()){
                    // 更新现有GPS分析数据段
                    currentSegment.setEndTime(gps.getGpsTime());
                    //得到时长(s)
                    Long duration = DateUtil.betweenMs(currentSegment.getEndTime(),currentSegment.getStartTime())/1000;
                    currentSegment.setDuration(duration);
                } else if(currentSegment == null){
                    currentSegment = createSegment(gps, vehicle,recordType,true);
                    segments.add(currentSegment);
                }
            }
        }
        return segments;
    }

    @Override
    public List<VehicleWorkVO> findAnalysisByTimeAndVehicle(List<String> numberplates, Date startTime, Date endTime) {
        return gisGpsAnalysisMapper.findAnalysisByTimeAndVehicle(numberplates,startTime,endTime);
    }

    @Override
    public IPage<StatisGisVo> statisGis(Page<StatisGisVo> page, EverydayQuery query) {
        checkTime(query);
        ArrayList<StatisGisVo> statisGisVoArrayList = new ArrayList<>();
        LambdaQueryWrapper<GisGpsAnalysis> lambdaQueryWrapper = QueryGenerator.initQueryWrapper(GisGpsAnalysis.class, query);
        List<GisGpsAnalysis> list = list(lambdaQueryWrapper);
        for (GisGpsAnalysis gisGpsAnalysis : list) {
            Date ownDate = gisGpsAnalysis.getOwnDate();
            if ("1".equals(query.getStatisType())) {
                gisGpsAnalysis.setStatisTime(DateUtil.formatDate(ownDate));
            } else if ("2".equals(query.getStatisType())) {
                int week = DateUtil.dateNew(ownDate).setFirstDayOfWeek(Week.MONDAY).weekOfYear();
                int year = DateUtil.year(ownDate);
                gisGpsAnalysis.setStatisTime(StrUtil.format("{}年第{}周",year,week));
            } else if ("3".equals(query.getStatisType())) {
                gisGpsAnalysis.setStatisTime(DateUtil.format(ownDate, "yyyy-MM"));
            } else {
                gisGpsAnalysis.setStatisTime(DateUtil.formatDate(ownDate));
            }
        }
        Map<String, Map<String, List<GisGpsAnalysis>>> collect = list.stream().collect(Collectors.groupingBy(GisGpsAnalysis::getStatisTime, Collectors.groupingBy(GisGpsAnalysis::getNumberPlate)));
        for (Map.Entry<String, Map<String, List<GisGpsAnalysis>>> dateMapEntry : collect.entrySet()) {
            for (Map.Entry<String, List<GisGpsAnalysis>> stringListEntry : dateMapEntry.getValue().entrySet()) {
                //上次日期
                Date date = null;
                Integer days = 0;
                List<GisGpsAnalysis> value = stringListEntry.getValue();
                GisGpsAnalysis first = value.get(0);
                StatisGisVo statisGisVo = new StatisGisVo();
                statisGisVo.setProjectId(first.getProjectId());
                statisGisVo.setPlateNumber(first.getNumberPlate());
                statisGisVo.setVehicleType(first.getVehicleType());
                statisGisVo.setVehicleBrand(first.getVehicleBrand());
                statisGisVo.setVehicleModel(first.getVehicleModel());
                statisGisVo.setStatisTime(first.getStatisTime());
                if ("1".equals(query.getStatisType())) {
                    statisGisVo.setRequiredAttendanceDays(1);
                } else if ("2".equals(query.getStatisType())) {
                    statisGisVo.setRequiredAttendanceDays(7);
                } else if ("3".equals(query.getStatisType())) {
                    statisGisVo.setRequiredAttendanceDays(DateUtil.lengthOfMonth(DateUtil.month(first.getOwnDate())+1,DateUtil.isLeapYear(DateUtil.year(first.getOwnDate()))));
                } else {
                    statisGisVo.setRequiredAttendanceDays(1);
                }
                //行驶里程
                BigDecimal totalMile = BigDecimal.ZERO;
                //作业里程
                BigDecimal workMile = BigDecimal.ZERO;
                //行驶时间
                BigDecimal totalTime = BigDecimal.ZERO;
                //作业时间
                BigDecimal workTime = BigDecimal.ZERO;
                //行驶作业时间
                BigDecimal travelWorkTime = BigDecimal.ZERO;
                //停靠作业时长
                BigDecimal stopWorkTime = BigDecimal.ZERO;
                //平均行驶速度
                BigDecimal avgSpeed;
                //平均作业速度
                BigDecimal avgWorkSpeed;
                //在线时长
                BigDecimal onlineTime = BigDecimal.ZERO;
                //通电停靠时长
                BigDecimal onlineStopTime = BigDecimal.ZERO;
                for (GisGpsAnalysis gisGpsAnalysis : value) {
                    if (date == null) {
                        date = gisGpsAnalysis.getOwnDate();
                        days = days + 1;
                    }
                    if (!DateUtil.isSameDay(gisGpsAnalysis.getOwnDate(),date)) {
                        date = gisGpsAnalysis.getOwnDate();
                        days = days + 1;
                    }
                    BigDecimal duration = BigDecimal.valueOf(gisGpsAnalysis.getDuration());
                    //计算在线时长
                    onlineTime = onlineTime.add(duration);
                    if (gisGpsAnalysis.getRecordType() == VehicleGpsType.Driving_Not_Work.getValue()) {
                        //行驶未作业
                        //计算行驶里程
                        totalMile = totalMile.add(gisGpsAnalysis.getMile());
                        //计算行驶时长
                        totalTime = totalTime.add(duration);
                    } else if (gisGpsAnalysis.getRecordType() == VehicleGpsType.Driving_Work.getValue()) {
                        //行驶作业
                        //计算行驶里程
                        totalMile = totalMile.add(gisGpsAnalysis.getMile());
                        //计算行驶时长
                        totalTime = totalTime.add(duration);
                        //计算作业里程
                        workMile = workMile.add(gisGpsAnalysis.getMile());
                        //计算作业时长
                        workTime = workTime.add(duration);
                        //计算行驶作业时长
                        travelWorkTime = travelWorkTime.add(duration);
                    } else if (gisGpsAnalysis.getRecordType() == VehicleGpsType.Stopped_Work.getValue()) {
                        //停靠作业
                        //计算作业里程
                        workMile = workMile.add(gisGpsAnalysis.getMile());
                        //计算作业时长
                        workTime = workTime.add(duration);
                        //计算停靠作业时长
                        stopWorkTime = stopWorkTime.add(duration);
                    } else if (gisGpsAnalysis.getRecordType() == VehicleGpsType.Stopped_NOT_Work.getValue()) {
                        //停靠未作业
                        onlineStopTime = onlineStopTime.add(duration);
                    }
                }
                if (totalTime.compareTo(BigDecimal.ZERO)>0) {
                    avgSpeed = NumberUtil.div(totalMile,NumberUtil.div(totalTime,3600)).setScale(2,BigDecimal.ROUND_HALF_UP);
                    statisGisVo.setAvgSpeed(avgSpeed);
                }
                if (travelWorkTime.compareTo(BigDecimal.ZERO)>0) {
                    avgWorkSpeed = NumberUtil.div(workMile,NumberUtil.div(workTime,3600)).setScale(2,BigDecimal.ROUND_HALF_UP);
                    statisGisVo.setAvgWorkSpeed(avgWorkSpeed);
                }
                statisGisVo.setTotalMile(totalMile.setScale(2,BigDecimal.ROUND_HALF_UP));
                statisGisVo.setWorkMile(workMile.setScale(2,BigDecimal.ROUND_HALF_UP));
                statisGisVo.setTotalTime(NumberUtil.div(totalTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                statisGisVo.setWorkTime(NumberUtil.div(workTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                statisGisVo.setTravelWorkTime(NumberUtil.div(travelWorkTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                statisGisVo.setStopWorkTime(NumberUtil.div(stopWorkTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                statisGisVo.setOnlineTime(NumberUtil.div(onlineTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                statisGisVo.setOnlineStopTime(NumberUtil.div(onlineStopTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                statisGisVo.setActualAttendanceDays(days);
                statisGisVo.setNotAttendanceDays(statisGisVo.getRequiredAttendanceDays() - statisGisVo.getActualAttendanceDays());
                if (statisGisVo.getWorkMile().compareTo(BigDecimal.ZERO)>0&&statisGisVo.getTotalMile().compareTo(BigDecimal.ZERO)>0) {
                    statisGisVo.setMileEfficiency(NumberUtil.mul(NumberUtil.div(statisGisVo.getWorkMile(),statisGisVo.getTotalMile()),100).setScale(2,BigDecimal.ROUND_HALF_UP));
                }
                if (statisGisVo.getWorkTime().compareTo(BigDecimal.ZERO)>0&&statisGisVo.getOnlineTime().compareTo(BigDecimal.ZERO)>0) {
                    statisGisVo.setTimeEfficiency(NumberUtil.mul(NumberUtil.div(statisGisVo.getWorkTime(),statisGisVo.getOnlineTime()),100).setScale(2,BigDecimal.ROUND_HALF_UP));
                }
                statisGisVoArrayList.add(statisGisVo);
            }
        }
        statisGisVoArrayList.sort(Comparator.comparing(StatisGisVo::getStatisTime).reversed());
        PageUtil.setOneAsFirstPageNo();
        int totalPage = PageUtil.totalPage(statisGisVoArrayList.size(), Convert.toInt(page.getSize()));
        if (page.getCurrent() > totalPage) {
            page.setCurrent(totalPage);
        }
        page.setTotal(statisGisVoArrayList.size());
        page.setPages(totalPage);
        Segment<Integer> integerSegment = PageUtil.toSegment(Convert.toInt(page.getCurrent()), Convert.toInt(page.getSize()));
        Integer endIndex = integerSegment.getEndIndex();
        if (endIndex > statisGisVoArrayList.size()) {
            endIndex = statisGisVoArrayList.size();
        }
        page.setRecords(statisGisVoArrayList.subList(integerSegment.getStartIndex(), endIndex));
        return page;
    }

    private List<GisGpsAnalysis> genAnalysisFuLongMa(List<TbGisGps> gpsList,TbVehicle vehicle,boolean isEic){
        List<GisGpsAnalysis> segments = new ArrayList<>();
        GisGpsAnalysis currentSegment = null;
//        List<TbGisGps> handleWorkList = processGpsData(gpsList);
        for (TbGisGps gps : gpsList) {
            // 根据规则判断记录类型
            int recordType;
            if (isDrivingWorkFlm(gps)) {
                recordType = VehicleGpsType.Driving_Work.getValue();
            } else if (isDrivingNotWorkFlm(gps)) {
                recordType = VehicleGpsType.Driving_Not_Work.getValue();
            } else if (isStoppedWorkFlm(gps)) {
                recordType = VehicleGpsType.Stopped_Work.getValue();
            } else if (isStoppedNotWorkFlm(gps)){
                recordType = VehicleGpsType.Stopped_NOT_Work.getValue();
            }else{
                continue;
            }
            if (currentSegment == null || recordType != currentSegment.getRecordType()) {
                // 如果当前分段为空或者记录类型不同，创建新的分段
                currentSegment = createSegment(gps, vehicle,recordType,isEic);
                segments.add(currentSegment);
            } else {
                // 否则，将当前GPS数据添加到当前分段
                updateSegment(currentSegment,vehicle, gps,isEic);
            }
        }
        return segments;
    }

    private List<GisGpsAnalysis> genAnalysisNormal(List<TbGisGps> gpsList,TbVehicle vehicle,boolean isEic){
        List<GisGpsAnalysis> segments = new ArrayList<>();
        GisGpsAnalysis currentSegment = null;
        for (TbGisGps gps : gpsList) {
            // 根据规则判断记录类型
            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 (currentSegment == null || recordType != currentSegment.getRecordType()) {
                // 如果当前分段为空或者记录类型不同，创建新的分段
                currentSegment = createSegment(gps, vehicle,recordType,isEic);
                segments.add(currentSegment);
            } else {
                // 否则，将当前GPS数据添加到当前分段
                updateSegment(currentSegment,vehicle, gps,isEic);
            }
        }
        return segments;
    }
    /**
     * 行驶作业 点火且速度大于0
     *
     * @param gps
     * @return
     */
    private boolean isDrivingWork(TbGisGps gps) {
        return gps.getAcc()%2 == 1 && gps.getSpeed().compareTo(BigDecimal.ZERO) > 0 && isWorking(gps);
    }

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


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

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

    private GisGpsAnalysis createSegment(TbGisGps gps,TbVehicle vehicle, int recordType,boolean isEic) {
        // 创建新的GPS分析数据段
        GisGpsAnalysis segment = new GisGpsAnalysis();

        segment.setProjectId(vehicle.getProjectId());
        segment.setVehicleBrand(vehicle.getVehicleBrand());
        segment.setVehicleModel(vehicle.getVehicleModel());
        segment.setVehicleType(vehicle.getVehicleType());
        segment.setNumberPlate(gps.getPlateNumber());
        segment.setOwnDate(gps.getGpsTime());
        segment.setStartTime(gps.getGpsTime());
        segment.setRecordType(recordType);
        segment.setStartMile(gps.getTotalMileage());
        segment.setStartSoc(gps.getStateOfCharge());
        segment.setCreateTime(new Date());
        segment.setMile(BigDecimal.ZERO);
        if(!isEic){
            segment.setLon(gps.getLon());
            segment.setLat(gps.getLat());
            segment.setMile(BigDecimal.ZERO);
        }
        updateSegment(segment,vehicle,gps,isEic);
        // 设置其他属性
        return segment;
    }

    private void updateSegment(GisGpsAnalysis segment,TbVehicle vehicle, TbGisGps gps,boolean isEic) {
        // 更新现有GPS分析数据段
        segment.setEndTime(gps.getGpsTime());
        //得到时长(s)
        Long duration = DateUtil.betweenMs(segment.getEndTime(),segment.getStartTime())/1000;
        segment.setDuration(duration);
        //如果为eic,则直接取里程，为808则计算里程
        if(isEic){
            Integer batteryCapacity = ObjectUtil.isEmpty(vehicle.getBatteryCapacity()) ? 0 : vehicle.getBatteryCapacity();
            //里程
            segment.setEndMile(gps.getTotalMileage());
            BigDecimal mile = segment.getEndMile().subtract(segment.getStartMile());
            segment.setMile(mile);
            //soc
            if(ObjectUtil.isNotEmpty(gps.getStateOfCharge())){
                segment.setEndSoc(gps.getStateOfCharge());
                double hour = Convert.toDouble(duration/3600);
                BigDecimal sub = NumberUtil.sub(segment.getStartSoc(), segment.getEndSoc());
                BigDecimal div = NumberUtil.div(sub, 100);
                BigDecimal mul = NumberUtil.mul(div, batteryCapacity);
                if (hour > 0) {
                    segment.setPowerConsume(NumberUtil.div(mul,hour,2));
                }
            }
            segment.setBatteryCapacity(new BigDecimal(batteryCapacity));
        }else{
           double distance =  CalcTool.getDistance(segment.getLon().doubleValue(),segment.getLat().doubleValue(),gps.getLon().doubleValue(),gps.getLat().doubleValue());
           if(segment.getDuration()>0 && distance /segment.getDuration() < 0.055 ){
               segment.setMile(segment.getMile().add(new BigDecimal(distance)));
               segment.setLon(gps.getLon());
               segment.setLat(gps.getLat());
           }
        }
    }

    public static List<TbGisGps> processGpsData(List<TbGisGps> gpsDataList) {
        if (gpsDataList == null || gpsDataList.isEmpty()) {
            return new ArrayList<>();
        }
        List<TbGisGps> processedList = new ArrayList<>();
        int isWork = 0;
        Date workStartTime = null;
        boolean withinWork = false;
        long timeThreshold = 1 * 60 * 1000;
        for (TbGisGps currentData : gpsDataList) {
            if (currentData.getSpeed().compareTo(new BigDecimal(2)) >= 0 && currentData.getSpeed().compareTo(new BigDecimal(20)) <= 0) {
                if (isWork == 0) {
                    if(!withinWork){
                        workStartTime = currentData.getGpsTime();
                        withinWork = true;
                    }else{
                        long timeDifference = currentData.getGpsTime().getTime() - workStartTime.getTime();
                        if(timeDifference >= timeThreshold){
                            isWork = 1;
                        }
                    }
                }
            } else {
                if (isWork == 1) {
                    long timeDifference = currentData.getGpsTime().getTime() - workStartTime.getTime();
                    if (timeDifference >= timeThreshold) {
                        isWork = 0;
                        withinWork = false;
                    }
                }
            }
            currentData.setIsWork(isWork);
            processedList.add(currentData);
        }
        return processedList;
    }

    private boolean isWorking(TbGisGps gps) {
        return Objects.nonNull(gps.getIsWork()) && gps.getIsWork() == 1;
//        // 增加一个次数变量来跟踪连续行驶的时间
//        int consecutiveDrivingTimeInSeconds = 0;
//        boolean isWorking = false;
//
//        // 检查GPS数据是否满足条件
//        if (gps.getAcc() % 2 == 1 && gps.getSpeed().compareTo(BigDecimal.ZERO) > 0) {
//            // 如果已经在作业状态，增加连续行驶时间
//            if (isWorking) {
//                consecutiveDrivingTimeInSeconds++;
//            } else {
//                // 否则，将状态设置为作业状态，并将时间重置为1
//                isWorking = true;
//                consecutiveDrivingTimeInSeconds = 1;
//            }
//
//            // 检查连续行驶时间是否达到2分钟
//            if (consecutiveDrivingTimeInSeconds >= 120) {
//                // 检查速度是否在2-20码之间
//                if (gps.getSpeed().compareTo(new BigDecimal(2)) >= 0 && gps.getSpeed().compareTo(new BigDecimal(20)) <= 0) {
//                    // 符合作业条件
//                    return true;
//                }
//            }
//        } else {
//            // 如果不满足条件，将状态重置为非作业状态
//            isWorking = false;
//            consecutiveDrivingTimeInSeconds = 0;
//        }
//        return false;
    }

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

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


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

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


    @Override
    public IPage<EverydayGisVo> everydayGis(Page<EverydayGisVo> page, EverydayQuery query) {
        checkTime(query);
        ArrayList<EverydayGisVo> gisVoArrayList = new ArrayList<>();
        LambdaQueryWrapper<GisGpsAnalysis> lambdaQueryWrapper = QueryGenerator.initQueryWrapper(GisGpsAnalysis.class, query);
        List<GisGpsAnalysis> list = list(lambdaQueryWrapper);
        Map<Date, Map<String, List<GisGpsAnalysis>>> collect1 = list.stream().collect(Collectors.groupingBy(GisGpsAnalysis::getOwnDate, Collectors.groupingBy(GisGpsAnalysis::getNumberPlate)));

        for (Map.Entry<Date, Map<String, List<GisGpsAnalysis>>> dateMapEntry : collect1.entrySet()) {
            for (Map.Entry<String, List<GisGpsAnalysis>> stringListEntry : dateMapEntry.getValue().entrySet()) {
                String key = stringListEntry.getKey();
                List<GisGpsAnalysis> value = stringListEntry.getValue();
                GisGpsAnalysis first = value.get(0);
                EverydayGisVo everydayGisVo = new EverydayGisVo();
                everydayGisVo.setProjectId(first.getProjectId());
                everydayGisVo.setPlateNumber(first.getNumberPlate());
                everydayGisVo.setVehicleType(first.getVehicleType());
                everydayGisVo.setVehicleBrand(first.getVehicleBrand());
                everydayGisVo.setVehicleModel(first.getVehicleModel());
                everydayGisVo.setStatisTime(DateUtil.formatDate(first.getOwnDate()));
                //行驶里程
                BigDecimal totalMile = BigDecimal.ZERO;
                //作业里程
                BigDecimal workMile = BigDecimal.ZERO;
                //行驶时间
                BigDecimal totalTime = BigDecimal.ZERO;
                //作业时间
                BigDecimal workTime = BigDecimal.ZERO;
                //行驶作业时间
                BigDecimal travelWorkTime = BigDecimal.ZERO;
                //停靠作业时长
                BigDecimal stopWorkTime = BigDecimal.ZERO;
                //平均行驶速度
                BigDecimal avgSpeed;
                //平均作业速度
                BigDecimal avgWorkSpeed;
                //作业开始时间
                Date workStartTime = null;
                //作业结束时间
                Date workEndTime = null;
                //在线时长
                BigDecimal onlineTime = BigDecimal.ZERO;
                //通电停靠时长
                BigDecimal onlineStopTime = BigDecimal.ZERO;
                for (GisGpsAnalysis gisGpsAnalysis : value) {
                    BigDecimal duration = BigDecimal.valueOf(gisGpsAnalysis.getDuration());
                    //计算在线时长
                    onlineTime = onlineTime.add(duration);
                    if (gisGpsAnalysis.getRecordType() == VehicleGpsType.Driving_Not_Work.getValue()) {
                        //行驶未作业
                        //计算行驶里程
                        totalMile = totalMile.add(gisGpsAnalysis.getMile());
                        //计算行驶时长
                        totalTime = totalTime.add(duration);
                    } else if (gisGpsAnalysis.getRecordType() == VehicleGpsType.Driving_Work.getValue()) {
                        //行驶作业
                        //计算行驶里程
                        totalMile = totalMile.add(gisGpsAnalysis.getMile());
                        //计算行驶时长
                        totalTime = totalTime.add(duration);
                        //计算作业里程
                        workMile = workMile.add(gisGpsAnalysis.getMile());
                        //计算作业时长
                        workTime = workTime.add(duration);
                        //计算行驶作业时长
                        travelWorkTime = travelWorkTime.add(duration);
                        //计算作业开始时间
                        if (workStartTime == null || workStartTime.after(gisGpsAnalysis.getStartTime())) {
                            workStartTime = gisGpsAnalysis.getStartTime();
                        }
                        //计算作业结束时间
                        if (gisGpsAnalysis.getEndTime() != null) {
                            if (workEndTime == null || workEndTime.before(gisGpsAnalysis.getEndTime())) {
                                workEndTime = gisGpsAnalysis.getEndTime();
                            }
                        }
                    } else if (gisGpsAnalysis.getRecordType() == VehicleGpsType.Stopped_Work.getValue()) {
                        //停靠作业
                        //计算作业里程
                        workMile = workMile.add(gisGpsAnalysis.getMile());
                        //计算作业时长
                        workTime = workTime.add(duration);
                        //计算停靠作业时长
                        stopWorkTime = stopWorkTime.add(duration);
                        //计算作业开始时间
                        if (workStartTime == null || workStartTime.after(gisGpsAnalysis.getStartTime())) {
                            workStartTime = gisGpsAnalysis.getStartTime();
                        }
                        //计算作业结束时间
                        if (gisGpsAnalysis.getEndTime() != null) {
                            if (workEndTime == null || workEndTime.before(gisGpsAnalysis.getEndTime())) {
                                workEndTime = gisGpsAnalysis.getEndTime();
                            }
                        }
                    } else if (gisGpsAnalysis.getRecordType() == VehicleGpsType.Stopped_NOT_Work.getValue()) {
                        //停靠未作业
                        onlineStopTime = onlineStopTime.add(duration);
                    }
                }
                if (totalTime.compareTo(BigDecimal.ZERO)>0) {
                    avgSpeed = NumberUtil.div(totalMile,NumberUtil.div(totalTime,3600)).setScale(2,BigDecimal.ROUND_HALF_UP);
                    everydayGisVo.setAvgSpeed(avgSpeed);
                }
                if (travelWorkTime.compareTo(BigDecimal.ZERO)>0) {
                    avgWorkSpeed = NumberUtil.div(workMile,NumberUtil.div(workTime,3600)).setScale(2,BigDecimal.ROUND_HALF_UP);
                    everydayGisVo.setAvgWorkSpeed(avgWorkSpeed);
                }
                everydayGisVo.setTotalMile(totalMile.setScale(2,BigDecimal.ROUND_HALF_UP));
                everydayGisVo.setWorkMile(workMile.setScale(2,BigDecimal.ROUND_HALF_UP));
                everydayGisVo.setTotalTime(NumberUtil.div(totalTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                everydayGisVo.setWorkTime(NumberUtil.div(workTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                everydayGisVo.setTravelWorkTime(NumberUtil.div(travelWorkTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                everydayGisVo.setStopWorkTime(NumberUtil.div(stopWorkTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                everydayGisVo.setWorkStartTime(workStartTime);
                everydayGisVo.setWorkEndTime(workEndTime);
                everydayGisVo.setOnlineTime(NumberUtil.div(onlineTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                everydayGisVo.setOnlineStopTime(NumberUtil.div(onlineStopTime,3600).setScale(2,BigDecimal.ROUND_HALF_UP));
                gisVoArrayList.add(everydayGisVo);
            }
        }
        gisVoArrayList.sort(Comparator.comparing(EverydayGisVo::getStatisTime));
        PageUtil.setOneAsFirstPageNo();
        int totalPage = PageUtil.totalPage(gisVoArrayList.size(), Convert.toInt(page.getSize()));
        if (page.getCurrent() > totalPage) {
            page.setCurrent(totalPage);
        }
        page.setTotal(gisVoArrayList.size());
        page.setPages(totalPage);
        Segment<Integer> integerSegment = PageUtil.toSegment(Convert.toInt(page.getCurrent()), Convert.toInt(page.getSize()));
        Integer endIndex = integerSegment.getEndIndex();
        if (endIndex > gisVoArrayList.size()) {
            endIndex = gisVoArrayList.size();
        }
        page.setRecords(gisVoArrayList.subList(integerSegment.getStartIndex(), endIndex));
        return page;
    }


    private static List<GisGpsAnalysis> mergeGisData(List<GisGpsAnalysis> inputList,boolean isEic) {
        if (inputList == null || inputList.isEmpty()) {
            return inputList;
        }
        List<GisGpsAnalysis> mergedList = new ArrayList<>();
        GisGpsAnalysis previousData = null;

        for (GisGpsAnalysis currentData : inputList) {
            if (previousData == null) {
                previousData = currentData;
            } else {
                if (areMergable(previousData, currentData,isEic)) {
                    mergeData(previousData, currentData,isEic);
                } else {
                    mergedList.add(previousData);
                    previousData = currentData;
                }
            }
        }
        if (previousData != null) {
            mergedList.add(previousData);
        }
        return mergedList;
    }

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

    private static boolean areMergable(GisGpsAnalysis previousData, GisGpsAnalysis currentData,boolean isEic) {
        if(isEic){
            return (!previousData.getRecordType().equals(currentData.getRecordType()) && currentData.getDuration() < 120 &&
                    (currentData.getRecordType() == 3 || currentData.getRecordType() == 4) &&
                    calculateTimeDifferenceInMinutes(previousData.getEndTime(), currentData.getStartTime()) <= 2)
                    || (previousData.getRecordType().equals(currentData.getRecordType()) && calculateTimeDifferenceInMinutes(previousData.getEndTime(), currentData.getStartTime()) <= 5 );
        }else{
            return (!previousData.getRecordType().equals(currentData.getRecordType()) && currentData.getDuration() < 120 &&
                    calculateTimeDifferenceInMinutes(previousData.getEndTime(), currentData.getStartTime()) <= 2)
                    || (previousData.getRecordType().equals(currentData.getRecordType()) && calculateTimeDifferenceInMinutes(previousData.getEndTime(), currentData.getStartTime()) <= 5 );
        }
    }

    private static void mergeData(GisGpsAnalysis previousData, GisGpsAnalysis currentData,boolean isEic) {
        previousData.setEndTime(currentData.getEndTime());

        Long duration = DateUtil.betweenMs(previousData.getEndTime(),previousData.getStartTime())/1000;
        previousData.setDuration(duration);
        if(isEic){
            previousData.setEndMile(currentData.getEndMile());
            BigDecimal totalMileage = previousData.getEndMile().subtract(previousData.getStartMile());
            previousData.setMile(totalMileage);
        }else{
            previousData.setMile(previousData.getMile().add(currentData.getMile()));
        }

//        previousData.setRecordType(currentData.getRecordType());

        if(ObjectUtil.isAllNotEmpty(previousData.getEndSoc(),currentData.getEndSoc())){
            previousData.setEndSoc(currentData.getEndSoc());
        }
        if(ObjectUtil.isAllNotEmpty(previousData.getPowerConsume(),currentData.getPowerConsume())){
            BigDecimal totalPowerConsume = previousData.getPowerConsume().add(currentData.getPowerConsume());
            previousData.setPowerConsume(totalPowerConsume);
        }else{
            previousData.setPowerConsume(previousData.getPowerConsume());
        }
    }

    private void checkTime(EverydayQuery query) {
        List<Date> time = query.getTime();
        if (ObjectUtil.isNotEmpty(time)) {
            if (time.size()!=2) {
                throw new GciBootException("查询必须包括起止时间！");
            } else {
                if (time.get(0).after(time.get(1))) {
                    throw new GciBootException("查询开始时间需要大于结束时间！");
                } else {
                    if (DateUtil.between(time.get(0),time.get(1), DateUnit.DAY) > 30) {
                        throw new GciBootException("查询间隔不能超过30天!");
                    }
                }
            }
        } else {
            throw new GciBootException("查询起止时间不能为空！");
        }
    }

    @Override
    public EverydayMileCountVo everydayMileCount(EverydayQuery query) {
        checkTime(query);
        LambdaQueryWrapper<GisGpsAnalysis> lambdaQueryWrapper = QueryGenerator.initQueryWrapper(GisGpsAnalysis.class, query,true);
        List<EverydayMileCountDto> everydayMileCountDtoList = gisGpsAnalysisMapper.everydayMileCount(lambdaQueryWrapper,query);
        BigDecimal totalMile = everydayMileCountDtoList.stream().filter(i ->i.getRecordType() == VehicleGpsType.Driving_Work.getValue() || i.getRecordType() == VehicleGpsType.Driving_Not_Work.getValue()).map(EverydayMileCountDto::getSum).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal workMile = everydayMileCountDtoList.stream().filter(i -> i.getRecordType() == VehicleGpsType.Driving_Work.getValue() || i.getRecordType() == VehicleGpsType.Stopped_Work.getValue()).map(EverydayMileCountDto::getSum).reduce(BigDecimal.ZERO, BigDecimal::add);
        EverydayMileCountVo vo = new EverydayMileCountVo();
        vo.setTotalMile(totalMile);
        vo.setWorkMile(workMile);
        return vo;
    }
}
