package com.clx.performance.service.impl.trace;

import com.clx.performance.esplus.mapper.DriverTraceESPlusMapper;
import com.clx.performance.esplus.mapper.TruckTraceESPlusMapper;
import com.clx.performance.esplus.model.DriverTraceESPlus;
import com.clx.performance.esplus.model.TruckTraceESPlus;
import com.clx.performance.param.temp.DriverTraceAddParam;
import com.clx.performance.param.temp.TruckTraceAddParam;
import com.clx.performance.service.trace.TruckTraceService;
import com.clx.performance.struct.trace.DriverTraceStruct;
import com.clx.performance.struct.trace.TruckTraceStruct;
import com.clx.performance.utils.LocalDateTimeUtils;
import com.clx.performance.vo.pc.trace.DriverTraceVO;
import com.clx.performance.vo.pc.trace.DriverTruckTraceVO;
import com.msl.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class TruckTraceServiceImpl implements TruckTraceService {
    // 停留速度
    private static final Integer PARK_SPEED = 5;

    @Autowired
    private TruckTraceESPlusMapper truckTraceESPlusMapper;
    @Autowired
    private DriverTraceESPlusMapper driverTraceESPlusMapper;

    @Autowired
    private TruckTraceStruct truckTraceStruct;
    @Autowired
    private DriverTraceStruct driverTraceStruct;

    private final static long stopMinutes = 5;

    @Override
    public List<DriverTruckTraceVO> listTruckTraceByTime(String truckNo, String beginTime, String endTime) {
        List<TruckTraceESPlus> list = truckTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<TruckTraceESPlus>()
                .eq(TruckTraceESPlus::getTruckNo, truckNo)
                .ge(TruckTraceESPlus::getGpsTime, beginTime)
                .le(TruckTraceESPlus::getGpsTime, endTime)
                .orderByAsc("gpsTime")
        );
        return truckTraceStruct.convert(list);
    }

    //计算车辆的停留结束时间
    @Override
    public List<DriverTruckTraceVO> calcTruckStayTime(List<DriverTruckTraceVO> traceList){
        // 停留时长计算
        Integer parkIdx = null;
        for (int i=0; i<traceList.size(); i++){
            if (traceList.get(i).getSpeed() == null){continue;}

            if (traceList.get(i).getSpeed().compareTo(new BigDecimal(PARK_SPEED)) < 0){
                if (parkIdx == null) {parkIdx = i;}
            }
            else {
                if (parkIdx == null) {continue;}
                long duration = Duration.between(LocalDateTimeUtils.parseTime(traceList.get(parkIdx).getGpsTime()),
                        LocalDateTimeUtils.parseTime(traceList.get(i).getGpsTime())).getSeconds();
                BigDecimal stayTime = new BigDecimal(duration)
                        .divide(new BigDecimal(60), 1, RoundingMode.HALF_UP);
                for (int j= parkIdx; j<=i; j++){
                    traceList.get(j).setStayTime(stayTime);
                    traceList.get(j).setStayBeginTime(traceList.get(parkIdx).getGpsTime());
                    traceList.get(j).setStayEndTime(traceList.get(i).getGpsTime());
                }
                parkIdx = null;
            }
        }

        if (parkIdx!=null && parkIdx < traceList.size()-1){
            long duration = Duration.between(LocalDateTimeUtils.parseTime(traceList.get(parkIdx).getGpsTime()),
                    LocalDateTimeUtils.parseTime(traceList.get(traceList.size()-1).getGpsTime())).getSeconds();
            BigDecimal stayTime = new BigDecimal(duration)
                    .divide(new BigDecimal(60), 1, RoundingMode.HALF_UP);
            for (int j= parkIdx; j<=traceList.size()-1; j++){
                traceList.get(j).setStayTime(stayTime);
                traceList.get(j).setStayBeginTime(traceList.get(parkIdx).getGpsTime());
                traceList.get(j).setStayEndTime(traceList.get(traceList.size()-1).getGpsTime());
            }
        }

        return traceList;
    }

    //计算司机的停留结束时间
    @Override
    public List<DriverTraceVO> calcDriverStayTime(List<DriverTraceVO> traceList){
        // 停留时长计算
        Integer parkIdx = null;
        for (int i=0; i<traceList.size(); i++){
            if (traceList.get(i).getSpeed() == null){continue;}

            if (traceList.get(i).getSpeed().compareTo(new BigDecimal(PARK_SPEED)) < 0){
                if (parkIdx == null) {parkIdx = i;}
            }
            else {
                if (parkIdx == null) {continue;}
                long duration = Duration.between(LocalDateTimeUtils.parseTime(traceList.get(parkIdx).getGpsTime()), LocalDateTimeUtils.parseTime(traceList.get(i).getGpsTime())).getSeconds();
                BigDecimal stayTime = new BigDecimal(duration)
                        .divide(new BigDecimal(60), 1, RoundingMode.HALF_UP);
                for (int j= parkIdx; j<=i; j++) {
                    traceList.get(j).setStayTime(stayTime);
                    traceList.get(j).setStayBeginTime(traceList.get(parkIdx).getGpsTime());
                    traceList.get(j).setStayEndTime(traceList.get(i).getGpsTime());
                }
                parkIdx = null;
            }
        }

        if (parkIdx!=null && parkIdx < traceList.size()-1){
            long duration = Duration.between(LocalDateTimeUtils.parseTime(traceList.get(parkIdx).getGpsTime()), LocalDateTimeUtils.parseTime(traceList.get(traceList.size()-1).getGpsTime())).getSeconds();
            BigDecimal stayTime = new BigDecimal(duration)
                    .divide(new BigDecimal(60), 1, RoundingMode.HALF_UP);
            for (int j= parkIdx; j<=traceList.size()-1; j++) {
                traceList.get(j).setStayTime(stayTime);
                traceList.get(j).setStayBeginTime(traceList.get(parkIdx).getGpsTime());
                traceList.get(j).setStayEndTime(traceList.get(traceList.size() - 1).getGpsTime());
            }
        }

        return traceList;
    }



    //如果停留时间超过指定时间，则切分轨迹信息为多段
    public List<List<DriverTruckTraceVO>> splitTruckTrace(List<DriverTruckTraceVO> traceList){
        List<List<DriverTruckTraceVO>> result = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(traceList)){
            if(traceList.size() == 1){
                result.add(traceList);
                return result;
            }else{
                List<DriverTruckTraceVO> temp = new ArrayList<>();
                temp.add(traceList.get(0));
                for(int i = 1; i< traceList.size(); i ++){
                    DriverTruckTraceVO current = traceList.get(i);
                    DriverTruckTraceVO prev = traceList.get(i-1);
                    long minutes = LocalDateTimeUtils.betweenMin(DateUtils.parseDateTime(prev.getGpsTime()).get(),
                            DateUtils.parseDateTime(current.getGpsTime()).get());
                    //如果两个坐标停留的时间超过指定时间，则为新的一段路线。
                    if(minutes > stopMinutes){
                        result.add(temp);
                        temp = new ArrayList<>();
                        temp.add(current);
                    }else{
                        temp.add(current);
                    }
                }
                result.add(temp);
            }
        }
        for (List<DriverTruckTraceVO> list : result) {
            calcTruckStayTime(list);
        }
        return result;
    }

    //如果停留时间超过指定时间，则切分轨迹信息为多段
    public List<List<DriverTraceVO>> splitDriverTrace(List<DriverTraceVO> driverTraceList){
        List<List<DriverTraceVO>> result = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(driverTraceList)){
            if(driverTraceList.size() == 1){
                result.add(driverTraceList);
                return result;
            }else{
                List<DriverTraceVO> temp = new ArrayList<>();
                temp.add(driverTraceList.get(0));
                for(int i = 1; i< driverTraceList.size(); i ++){
                    DriverTraceVO current = driverTraceList.get(i);
                    DriverTraceVO prev = driverTraceList.get(i-1);
                    long minutes = LocalDateTimeUtils.betweenMin(DateUtils.parseDateTime(prev.getGpsTime()).get(),
                            DateUtils.parseDateTime(current.getGpsTime()).get());
                    //如果两个坐标停留的时间超过指定时间，则为新的一段路线。
                    if(minutes > stopMinutes){
                        result.add(temp);
                        temp = new ArrayList<>();
                        temp.add(current);
                    }else{
                        temp.add(current);
                    }
                }
                result.add(temp);
            }
        }
        for (List<DriverTraceVO> list : result) {
            calcDriverStayTime(list);
        }
        return result;
    }



    @Override
    public List<DriverTraceVO> listDriverTraceByTime(Long userNo, String beginTime, String endTime) {
        List<DriverTraceESPlus> list = driverTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<DriverTraceESPlus>()
                .eq(DriverTraceESPlus::getUserNo, userNo)
                .ge(DriverTraceESPlus::getPositionTime, beginTime)
                .le(DriverTraceESPlus::getPositionTime, endTime)
                .orderByAsc("positionTime")
        );

       return driverTraceStruct.convert(list);
    }

    @Override
    public void truckTraceSave(TruckTraceAddParam param){

        List<TruckTraceESPlus> list = new ArrayList<>();
        for (TruckTraceAddParam.TruckTraceItem item : param.getTraceList()) {
            TruckTraceESPlus es = new TruckTraceESPlus();
            es.setTruckNo(item.getTruckNo());
            es.setAngle(item.getAngle());
            es.setHeight(item.getHeight());
            es.setLocation(item.getLocation());
            es.setGpsTime(item.getGpsTime());
            es.setMileage(item.getMileage());
            es.setSpeed(item.getSpeed());
            es.setCreateTime(LocalDateTimeUtils.formatTime());

            list.add(es);
        }

        truckTraceESPlusMapper.insertBatch(list);
    }
    @Override
    public void driverTraceSave(DriverTraceAddParam param){

        List<DriverTraceESPlus> list = new ArrayList<>();
        DriverTraceESPlus es = null;
        for (DriverTraceAddParam.DriverTraceItem item : param.getTraceList()) {
            es = new DriverTraceESPlus();
            es.setUserNo(item.getUserNo());
            es.setDataType(item.getDataType());
            es.setLocation(item.getLocation());
            es.setPositionTime(item.getGpsTime());
            es.setCreateTime(LocalDateTimeUtils.formatTime());
            es.setModifiedTime(es.getCreateTime());
            es.setSpeed(item.getSpeed());

            list.add(es);
        }

        driverTraceESPlusMapper.insertBatch(list);
    }

    @Override
    public BigDecimal[] getCurrentPosition(String truckNo, Long userNo) {
        String beginTime = LocalDateTimeUtils.formatTime(LocalDateTime.now().minusMinutes(5));
        String endTime = LocalDateTimeUtils.formatTime();

        // 车辆
        List<TruckTraceESPlus> list = truckTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<TruckTraceESPlus>()
                .eq(TruckTraceESPlus::getTruckNo, truckNo)
                .ge(TruckTraceESPlus::getGpsTime, beginTime)
                .le(TruckTraceESPlus::getGpsTime, endTime)
                .orderByDesc("gpsTime")
                .limit(1)
        );
        if (!list.isEmpty()){
            return list.get(0).getLocation();
        }

        // 司机
        List<DriverTraceESPlus> list1 = driverTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<DriverTraceESPlus>()
                .eq(DriverTraceESPlus::getUserNo, userNo)
                .ge(DriverTraceESPlus::getPositionTime, beginTime)
                .le(DriverTraceESPlus::getPositionTime, endTime)
                .orderByDesc("positionTime")
                .limit(1)
        );
        if (!list1.isEmpty()){
            return list1.get(0).getLocation();
        }

        return null;
    }

    @Override
    public int getParkTime(String truckNo, Long userNo, LocalDateTime beginDateTime) {
        return getParkTime(truckNo, userNo, beginDateTime, LocalDateTime.now());
    }

    @Override
    public int getParkTime(String truckNo, Long userNo, LocalDateTime beginDateTime, LocalDateTime endDateTime) {
        String beginTime = LocalDateTimeUtils.formatTime(beginDateTime);
        String endTime = LocalDateTimeUtils.formatTime(endDateTime);

        boolean flag = false;

        // 车辆
        // 非停留最大时间
        List<TruckTraceESPlus> list = truckTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<TruckTraceESPlus>()
                .eq(TruckTraceESPlus::getTruckNo, truckNo)
                .ge(TruckTraceESPlus::getSpeed, PARK_SPEED)
                .ge(TruckTraceESPlus::getGpsTime, beginTime)
                .le(TruckTraceESPlus::getGpsTime, endTime)
                .orderByDesc("gpsTime")
                .limit(1)
        );

        String time = beginTime;
        if (!list.isEmpty()){
            time = list.get(0).getGpsTime();
            flag = true;        //车辆存在轨迹
        }
        // 停留时间段
        list = truckTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<TruckTraceESPlus>()
                .eq(TruckTraceESPlus::getTruckNo, truckNo)
                .lt(TruckTraceESPlus::getSpeed, PARK_SPEED)
                .gt(TruckTraceESPlus::getGpsTime, time)
                .le(TruckTraceESPlus::getGpsTime, endTime)
                .orderByDesc("gpsTime")
        );
        if (!list.isEmpty()) {
            // 停留时长
            return (int)Duration.between(LocalDateTimeUtils.parseTime(list.get(list.size() - 1).getGpsTime()), LocalDateTimeUtils.parseTime(list.get(0).getGpsTime())).toMinutes();
        }
        if (flag) {return 0;}

        // 司机
        List<DriverTraceESPlus> list1 = driverTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<DriverTraceESPlus>()
                .eq(DriverTraceESPlus::getUserNo, userNo)
                .ge(DriverTraceESPlus::getSpeed, PARK_SPEED)
                .ge(DriverTraceESPlus::getPositionTime, beginTime)
                .le(DriverTraceESPlus::getPositionTime, endTime)
                .orderByDesc("positionTime")
                .limit(1)
        );
        time = beginTime;
        if (!list1.isEmpty()){time = list1.get(0).getPositionTime();}
        list1 = driverTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<DriverTraceESPlus>()
                .eq(DriverTraceESPlus::getUserNo, userNo)
                .lt(DriverTraceESPlus::getSpeed, PARK_SPEED)
                .gt(DriverTraceESPlus::getPositionTime, time)
                .le(DriverTraceESPlus::getPositionTime, endTime)
                .orderByDesc("positionTime")
        );
        if (!list1.isEmpty()) {
            return (int) Duration.between(LocalDateTimeUtils.parseTime(list1.get(list1.size() - 1).getPositionTime()), LocalDateTimeUtils.parseTime(list1.get(0).getPositionTime())).toMinutes();
        }

        return 0;
    }


    @Override
    public BigDecimal[] getCurrentTruckPosition(String truckNo) {
        String beginTime = LocalDateTimeUtils.formatTime(LocalDateTime.now().minusMinutes(5));
        String endTime = LocalDateTimeUtils.formatTime();

        // 车辆
        List<TruckTraceESPlus> list = truckTraceESPlusMapper.selectList(new LambdaEsQueryWrapper<TruckTraceESPlus>()
                .eq(TruckTraceESPlus::getTruckNo, truckNo)
                .ge(TruckTraceESPlus::getGpsTime, beginTime)
                .le(TruckTraceESPlus::getGpsTime, endTime)
                .orderByDesc("gpsTime")
                .limit(1)
        );
        if (CollectionUtils.isNotEmpty(list)){
            return list.get(0).getLocation();
        }
        return null;
    }


    /**
     * 生成指定大小的车辆轨迹数据插入到es中
     **/
    @Override
    public void saveTruckTrace(String truckNo,Integer size,String gpsTime){
        if(size > 20000){
            size = 20000;
        }
        List<TruckTraceESPlus> esList = new ArrayList<>();
        for (int i = 0 ;i < size ; i ++ ) {
            TruckTraceESPlus es = new TruckTraceESPlus();
            es.setTruckNo(truckNo);
            es.setAngle(new BigDecimal("1"));
            es.setHeight(null);
            es.setLocation(new BigDecimal[]{new BigDecimal("116.473705"), new BigDecimal("40.004198")});
            if(StringUtils.isNotBlank(gpsTime)){
                es.setGpsTime(gpsTime);
            }else{
                es.setGpsTime(DateUtils.formatDateTime(LocalDateTime.now()).get());
            }
            es.setMileage(null);
            es.setSpeed(new BigDecimal("3"));
            esList.add(es);
        }
        // 保存
        truckTraceESPlusMapper.insertBatch(esList);
    }



}
