package com.base.plan.service;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.base.api.client.PayloadRpcService;
import com.base.api.client.SatelliteRpcService;
import com.base.api.client.TragetDemandRpcService;
import com.base.api.client.TrbutDataRpcService;
import com.base.api.model.PayloadRpcDTO;
import com.base.api.model.TargetDemandRpcDTO;
import com.base.orbit.Exception.ServiceException;
import com.base.orbit.domain.AjaxResult;
import com.base.orbit.utils.DateUtils;
import com.base.plan.constant.PassTypeEnum;
import com.base.plan.mapper.*;
import com.base.plan.model.dto.SatelliteIdAndPayloadDTO;
import com.base.plan.model.dto.request.QueryDemandDTO;
import com.base.plan.model.dto.request.QueryKjckDTO;
import com.base.plan.model.entity.*;
import com.base.plan.model.param.DemandParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.Duration;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 筹划方案处理类
 *
 * @author xj
 * @date 2024/07/03 13:44:44
 */
@Service
@Slf4j
public class DemandService {

    @Resource
    private TragetDemandRpcService tragetDemandRpcService;
    @Resource
    private AlgSchemeStepWindowMapper algSchemeStepWindowMapper;
    @Resource
    private StripService stripService;
    @Resource
    private TrbutDataRpcService trbutDataRpcService;
    @Resource
    private SatelliteRpcService satelliteRpcService;
    @Resource
    private StepWindowRelationMapper stepWindowRelationMapper;
    @Resource
    private AlgSchemeWindowPassMapper algSchemeWindowPassMapper;
    @Resource
    private StepWindowDataPassRelationService stepWindowDataPassRelationService;
    @Resource
    private StepWindowControlPassRelationService stepWindowControlPassRelationService;
    @Resource
    private AlgSchemeStepMapper algSchemeStepMapper;
    @Resource
    private PayloadRpcService payloadRpcService;
    @Resource
    private AlgSchemeStepWindowService algSchemeStepWindowService;

    private static final String DEMAND_TYPE_FULL = "1";
    
    private static final int IS_CONFIRMED = 2;

    public void demandSubmit(Set<String> parentIds) {
        if (CollectionUtils.isEmpty(parentIds)) {
            throw new ServiceException("目标parentIds不能为空!");
        }
        List<TargetDemandRpcDTO> targetDemandRpcDTOS = tragetDemandRpcService.selTargetDemandByParentId(parentIds);
        log.info("需求提报，目标个数为:" + targetDemandRpcDTOS.size() + "");

        //  计算条带信息开始
        stripService.getPayloadInfosByReqId(targetDemandRpcDTOS);
        //  计算条带信息结束
        //  将parentId下所有需求状态改为已提交
        targetDemandRpcDTOS.forEach(targetRelation -> targetRelation.setStatus(1));
        tragetDemandRpcService.batchStatus(targetDemandRpcDTOS);
    }

    /**
     * 需求筹划
     *
     * @param parentIds 需求唯一标识
     * @return 筹划异步响应
     */
//    public void demandPlan(Set<String> parentIds) {
//        //  1.根据任务id查询批次下所有目标，parentId由前后端拼接而成，前三段由后端生成(以此作为同一批次依据) 最后一段由前端自行拼接(区分地图点选\目标库导入)，excel导入例外任务id均有后端生成
//        List<TargetDemandRpcDTO> targetRelationDTOList = tragetDemandRpcService.selTargetDemandByParentId(parentIds);
//        if (CollectionUtils.isEmpty(targetRelationDTOList)) return;
//        for (TargetDemandRpcDTO targetDemandRpcDTO : targetRelationDTOList) {
//            //  2.每一种目标都需指定卫星进行观测
//            List<SatelliteIdAndPayloadDTO> satelliteIdAndPayloadDTOS = stripService.satelliteIdCastDTO(targetDemandRpcDTO.getSatelliteSelected());
//            for (SatelliteIdAndPayloadDTO satelliteIdAndPayloadDTO : satelliteIdAndPayloadDTOS) {
//                // 3.根据目标\观测卫星查询筹划方案(实际上就是该目标生成的条带)，没有筹划方案则说明观测卫星对该目标不存在条带
//                List<AlgSchemeStepWindow> stepWindows = algSchemeStepWindowMapper.selectByParentIdAndSatellileId(targetDemandRpcDTO.getParentId(), satelliteIdAndPayloadDTO.getSatelliteId());
//                if (!CollectionUtils.isEmpty(stepWindows)) {
//                    List<QueryKjckDTO> kjckDTOList = getKjckDTOList(stepWindows, satelliteIdAndPayloadDTO);
//                    // 4.一个需求目标对应了多个可见窗口(条带)
//                    QueryDemandDTO queryDemandDTO = QueryDemandDTO
//                            .builder()
//                            .id(targetDemandRpcDTO.getId())
//                            .startTime(targetDemandRpcDTO.getStartTime())
//                            .endTime(targetDemandRpcDTO.getEndTime())
//                            .rate(targetDemandRpcDTO.getRate())
//                            .priority(targetDemandRpcDTO.getPriority())
//                            .parentIds(targetDemandRpcDTO.getParentId())
//                            .kjckDTOList(kjckDTOList)
//                            .targetCode(targetDemandRpcDTO.getTargetCode())
//                            .targetName(targetDemandRpcDTO.getTargetName())
//                            .build();
//                    // 筹划，并绑定窗口的筹划结果id
//                    ch(queryDemandDTO, stepWindows);
//                    // 筹划完成后，将需求修改为已筹划
//                    targetRelationDTOList.forEach(targetRelation -> targetRelation.setStatus(2));
//                    tragetDemandRpcService.batchStatus(targetRelationDTOList);
//                }
//            }
//        }
//    }
    public void demandPlan(Set<String> parentIds) {
        //  1.根据任务id查询批次下所有目标，parentId由前后端拼接而成，前三段由后端生成(以此作为同一批次依据) 最后一段由前端自行拼接(区分地图点选\目标库导入)，excel导入例外任务id均有后端生成
        List<TargetDemandRpcDTO> targetRelationDTOList = tragetDemandRpcService.selTargetDemandByParentId(parentIds);
        if (CollectionUtils.isEmpty(targetRelationDTOList)) return;
        for (TargetDemandRpcDTO targetDemandRpcDTO : targetRelationDTOList) {
            //  2.每一种目标都需指定卫星进行观测
            List<SatelliteIdAndPayloadDTO> satelliteIdAndPayloadDTOS = stripService.satelliteIdCastDTO(targetDemandRpcDTO.getSatelliteSelected());
            List<QueryKjckDTO> kjckDTOListAll = new ArrayList<>();
            for (SatelliteIdAndPayloadDTO satelliteIdAndPayloadDTO : satelliteIdAndPayloadDTOS) {
                // 3.根据目标\观测卫星查询筹划方案(实际上就是该目标生成的条带)，没有筹划方案则说明观测卫星对该目标不存在条带
                List<AlgSchemeStepWindow> stepWindows = algSchemeStepWindowMapper.selectByParentIdAndSatellileId(targetDemandRpcDTO.getParentId(), satelliteIdAndPayloadDTO.getSatelliteId());
                if (!CollectionUtils.isEmpty(stepWindows)) {
                    //  观测任务中一个观测目标存在多个观测卫星可见窗口
                    List<QueryKjckDTO> kjckDTOList = getKjckDTOList(stepWindows, satelliteIdAndPayloadDTO);
                    kjckDTOListAll.addAll(kjckDTOList);
                }
            }
            // 4.一个需求目标对应了多个卫星多个可见窗口(条带)
            QueryDemandDTO queryDemandDTO = QueryDemandDTO
                    .builder()
                    .id(targetDemandRpcDTO.getId())
                    .startTime(targetDemandRpcDTO.getStartTime())
                    .endTime(targetDemandRpcDTO.getEndTime())
                    .rate(targetDemandRpcDTO.getRate())
                    .priority(targetDemandRpcDTO.getPriority())
                    .parentIds(targetDemandRpcDTO.getParentId())
                    .kjckDTOList(kjckDTOListAll)
                    .targetCode(targetDemandRpcDTO.getTargetCode())
                    .targetName(targetDemandRpcDTO.getTargetName())
                    .build();
            // 筹划，并绑定窗口的筹划结果id
            ch(queryDemandDTO, kjckDTOListAll);
            // 筹划完成后，将需求修改为已筹划
            targetRelationDTOList.forEach(targetRelation -> targetRelation.setStatus(2));
            tragetDemandRpcService.batchStatus(targetRelationDTOList);
        }
    }

    /**
     * 筹划
     *
     * @param demandDTO   包含可见窗口需求对象
     * @param stepWindows 可见窗口
     */
    public void ch(QueryDemandDTO demandDTO, List<QueryKjckDTO> stepWindows) {
        String algId = UUID.randomUUID().toString();
        /**
         *    第一档需求（优先级小于40）必须满足数传时效性，如果不满足则标记该该可见窗口<不满足时效性></>
         *    是否满足数传时效性依据，根据需求中取出可见窗口访问时间区间，
         *    判断该时间区间内是否包含数传窗口，如果是则+1 否则赋值为0
         */
//        if (demandDTO.getPriority() < 40) {
//            boolean b = vaildPriority(demandDTO);
//            if (!b) {
//                demandDTO.setRemark("不满足时效性");
//                return;
//            }
//        }
        //  频次决定观测卫星数量
        Integer rate = demandDTO.getRate();
        //  可见窗口中卫星个数，如果频次大于观测卫星个数，说明该需求中观测卫星不足，需求任务无法完成
        Integer satelliteNum = stepWindows.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(QueryKjckDTO::getSatelliteId))), ArrayList::new)).size();
        if (rate > satelliteNum) {
            throw new ServiceException("成像窗口中观测卫星数无法满足任务所需观测频次");
        }

        //  1.取出该需求下的所有可见窗口
        List<QueryKjckDTO> kjckDTOList = demandDTO.getKjckDTOList();
        //  获取所有窗口id进行是否被选中判别
        List<QueryKjckDTO> kjckDTOSelected = isSelected(kjckDTOList);
        Assert.isTrue(!Objects.isNull(kjckDTOSelected), "需求id[" + demandDTO.getId() + "]未查询到可见窗口");
        //  2.获取条带中所有的卫星
        Set<String> satelliteCodes = kjckDTOSelected.stream().map(QueryKjckDTO::getSatelliteCode).collect(Collectors.toSet());
        Assert.isTrue(!CollectionUtils.isEmpty(satelliteCodes), "可见窗口中卫星id为空");
        //  3.根据卫星id查询载荷信息
        List<PayloadRpcDTO> payloadRpcDTOS = payloadRpcService.getAllList();
        List<PayloadRpcDTO> payloadRpcDTOList = payloadRpcDTOS.stream().filter(payloadRpcDTO -> satelliteCodes.contains(payloadRpcDTO.getSatelliteCode())).collect(Collectors.toList());

        //  todo 挨个查询每个不冲突的成像窗口，如果窗口的时间包含在该窗口对应卫星的数传资源时间段内，赋值为1，否则赋值为0
//        ChVO chVO = calSocre(demandDTO);
        /**
         * 将每个可见窗口进行打分后，进行排序再根据频次进行可见窗口取舍
         * 如:一个目标观测频次为3，可见窗口为5 则挑选分值最高的3个窗口作为优选
         */
        //  建立方案评估信息与可见窗口中间表关联关系
        for (QueryKjckDTO kjckDTO : kjckDTOSelected) {
            StepWindowRelation stepWindowRelation = StepWindowRelation.builder().createTime(DateUtils.getNowDate()).stepId(algId).isConfirm(kjckDTO.getIsConfirm()).windowId(kjckDTO.getWindowId()).build();
            stepWindowRelationMapper.insert(stepWindowRelation);
        }
        /**
         * 可见窗口对应多个测控列表和数传列表
         */
        Set<String> windows = kjckDTOSelected.stream().map(QueryKjckDTO::getWindowId).collect(Collectors.toSet());
        //  获取确认的条带窗口
        List<QueryKjckDTO> confirmKjckDTOS = kjckDTOSelected.stream().filter(kjckDTO -> kjckDTO.getIsConfirm().equals(2)).collect(Collectors.toList());

        //  每一个窗口对应测控\数传弧段
        List<AlgSchemeWindowPass> algSchemeWindowPassesCkzs = algSchemeWindowPassMapper.selectAlgSchemeWindowPassListByWindowIds(windows, 1);
        //  测控站资源
        List<StepWindowControlPassRelation> stepWindowControlPassCzRelations = new ArrayList<>();
        for (AlgSchemeWindowPass algSchemeWindowPassCkz : algSchemeWindowPassesCkzs) {
            StepWindowControlPassRelation stepWindowControlPassRelation = StepWindowControlPassRelation.builder().stepId(algId).windowId(algSchemeWindowPassCkz.getSchemeStepWindowId()).controlStationWindowId(algSchemeWindowPassCkz.getId()).createTime(DateUtils.getNowDate()).isConfirm(1).build();
            stepWindowControlPassCzRelations.add(stepWindowControlPassRelation);
        }
        //  确认的条带默认勾选第一个站作为弧段
        Map<String, List<StepWindowControlPassRelation>> controlPassDTOS = stepWindowControlPassCzRelations.stream().collect(Collectors.groupingBy(StepWindowControlPassRelation::getWindowId));
        Iterator<Map.Entry<String, List<StepWindowControlPassRelation>>> controlPassIterator = controlPassDTOS.entrySet().iterator();
        while (controlPassIterator.hasNext()) {
            Map.Entry<String, List<StepWindowControlPassRelation>> windowIdKeys = controlPassIterator.next();
            String windowIdKey = windowIdKeys.getKey();
            if (confirmKjckDTOS.stream().allMatch(confirmKjckDTO -> confirmKjckDTO.getWindowId().equals(windowIdKey))) {
                windowIdKeys.getValue().get(0).setIsConfirm(2);
            }
        }
        stepWindowControlPassRelationService.saveBatch(stepWindowControlPassCzRelations);
        //  数传站资源
        List<StepWindowDataPassRelation> stepWindowDataPassScRelations = new ArrayList<>();
        List<AlgSchemeWindowPass> algSchemeWindowPassesSczs = algSchemeWindowPassMapper.selectAlgSchemeWindowPassListByWindowIds(windows, 2);
        for (AlgSchemeWindowPass algSchemeWindowPassesScz : algSchemeWindowPassesSczs) {
            StepWindowDataPassRelation stepWindowDataPassRelation = StepWindowDataPassRelation.builder().createTime(DateUtils.getNowDate()).stepId(algId).windowId(algSchemeWindowPassesScz.getSchemeStepWindowId()).dataStationWindowId(algSchemeWindowPassesScz.getId()).isConfirm(1).build();
            stepWindowDataPassScRelations.add(stepWindowDataPassRelation);
        }
        Map<String, List<StepWindowDataPassRelation>> dataPassDTOS = stepWindowDataPassScRelations.stream().collect(Collectors.groupingBy(StepWindowDataPassRelation::getWindowId));
        Iterator<Map.Entry<String, List<StepWindowDataPassRelation>>> dataPassIterator = dataPassDTOS.entrySet().iterator();
        while (dataPassIterator.hasNext()) {
            Map.Entry<String, List<StepWindowDataPassRelation>> windowIdKeys = dataPassIterator.next();
            String windowIdKey = windowIdKeys.getKey();
            if (confirmKjckDTOS.stream().allMatch(dataPassDTO -> dataPassDTO.getWindowId().equals(windowIdKey))) {
                windowIdKeys.getValue().get(0).setIsConfirm(2);
            }
        }
        stepWindowDataPassRelationService.saveBatch(stepWindowDataPassScRelations);
        AlgSchemeStep algSchemeStep = assembleStepVo(algId, demandDTO, kjckDTOSelected, payloadRpcDTOList);
        //  将数据存入方法评估信息
        algSchemeStepMapper.insert(algSchemeStep);
        //  将筹划出来的可见窗口绑定筹划结果id
        algSchemeStepWindowMapper.setStepIdByWindowId(stepWindows.stream().map(QueryKjckDTO::getWindowId).collect(Collectors.toSet()), algSchemeStep.getId());
    }


    /**
     * 获取到所有可见窗口后，取出窗口集合查询条带表，获取是否被选中参数
     *
     * @param kjckDTOList
     * @return
     */
    public List<QueryKjckDTO> isSelected(List<QueryKjckDTO> kjckDTOList) {
        //  1.获取所有的窗口集合
        List<String> windowIds = kjckDTOList.stream().map(QueryKjckDTO::getWindowId).collect(Collectors.toList());
        //  2.根据窗口集合查询条带表
        QueryWrapper<StripInfo> stripInfoQueryWrapper = new QueryWrapper<>();
        stripInfoQueryWrapper.in("window_id", windowIds);
        List<StripInfo> stripInfos = stripService.list(stripInfoQueryWrapper);
        if (!CollectionUtils.isEmpty(stripInfos)) {
            for (QueryKjckDTO kjckDTO : kjckDTOList) {
                //  3.根据条带窗口id进行匹配
                Optional<StripInfo> stripInfoOptional = stripInfos.stream().filter(stripInfo -> stripInfo.getWindowId().equals(kjckDTO.getWindowId())).findFirst();
                if (stripInfoOptional.isPresent()) {
                    StripInfo stripInfo = stripInfoOptional.get();
                    kjckDTO.setIsConfirm(stripInfo.getIsSelected());
                }
            }
        }
        return kjckDTOList;
    }

    /**
     * 根据条带\目标信息\载荷构造方案评估信息列表
     *
     * @param algId             生成uuid
     * @param demandDTO         需求对象
     * @param kjckDTOS          带分值的可见窗口
     * @param payloadRpcDTOList 载荷集合
     * @return
     */
    public AlgSchemeStep assembleStepVo(String algId, QueryDemandDTO demandDTO, List<QueryKjckDTO> kjckDTOS, List<PayloadRpcDTO> payloadRpcDTOList) {
        //  最大观测间隔
        long maxTimeInterVal = kjckDTOS.stream().mapToLong(QueryKjckDTO::getTimeInterVal).max().getAsLong();
        //  最小观测间隔
        long minTimeInterVal = kjckDTOS.stream().mapToLong(QueryKjckDTO::getTimeInterVal).min().getAsLong();
        //  计算窗口所跨天数
        QueryKjckDTO start = kjckDTOS.stream().min(Comparator.comparing(QueryKjckDTO::getStartTime)).get();
        QueryKjckDTO end = kjckDTOS.stream().max(Comparator.comparing(QueryKjckDTO::getEndTime)).get();
        //  窗口中最早的观测时间
        QueryKjckDTO earliyTime = kjckDTOS.stream().min(Comparator.comparing(QueryKjckDTO::getVisitDateTime)).get();
        Date earilyTime = earliyTime.getVisitDateTime();
        //  最大分辨率
        double maxResolution = payloadRpcDTOList.stream().mapToDouble(YxInfoSatellitePayload -> YxInfoSatellitePayload.getGroundResolution().floatValue()).max().getAsDouble();
        //  最小分辨率
        double minResolution = payloadRpcDTOList.stream().mapToDouble(YxInfoSatellitePayload -> YxInfoSatellitePayload.getGroundResolution().floatValue()).min().getAsDouble();
        //  卫星数量,针对卫星id进行去重
        ArrayList<QueryKjckDTO> dtos = kjckDTOS.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(QueryKjckDTO::getSatelliteId))), ArrayList::new));
        //  跨天计算
        int days = DateUtils.differentDaysByMillisecond(start.getStartTime(), end.getEndTime()) == 0 ? 1 : DateUtils.differentDaysByMillisecond(start.getStartTime(), end.getEndTime());
        List<String> types = new ArrayList<>();
        //  观测手段
        kjckDTOS.forEach(
                p -> {
                    if (!types.contains(p.getType())) {
                        types.add(p.getType());
                    }
                });
        int timeVal = kjckDTOS.size();
        AlgSchemeStep algSchemeStep = AlgSchemeStep.builder()
                .id(algId).earliestObservationTime(earilyTime).createdDate(DateUtils.getNowDate())
                .maxScoutFre(new BigDecimal(maxTimeInterVal / 1000)).minScoutFre(new BigDecimal(minTimeInterVal / 1000)).loadType(types.toString())
                .observeNum(new BigDecimal(timeVal)).priority(demandDTO.getPriority()).daily(new BigDecimal(timeVal / days)).satCount(dtos.size()).stepName(demandDTO.getTargetName())
                .maxLoadResolution(new BigDecimal(maxResolution)).minLoadResolution(new BigDecimal(minResolution)).isConfirm(2)
                .build();

        return algSchemeStep;
    }


    /**
     * 筹划核心->针对需求中每个可见窗口进行打分，并选取地面站
     *
     * @param demandDTO 需求对象
     * @return
     */
//    public ChVO calSocre(QueryDemandDTO demandDTO) {
//        ChVO chVO = new ChVO();
//        List<QueryScoreKjckDTO> list = Lists.newArrayList();
//        //  取出可见窗口
//        List<QueryKjckDTO> kjckDTOList = demandDTO.getKjckDTOList();
//        for (QueryKjckDTO kjckDTO : kjckDTOList) {
//            // 重点任务落实率打分
//            byte implementScore = 0;
//            //  查询数传、测控弧段(访问时间作为开始时间，需求结束时间)
//            CxStationRpcDTO scStationVo = CxStationRpcDTO.builder().satelliteId(kjckDTO.getSatelliteId()).cxStartTime(DateUtils.addMinute(kjckDTO.getVisitDateTime())).cxEndTime(DateUtils.addHour(demandDTO.getEndTime())).build();
//            //  如果能查询出结果，说明该卫星访问时间段是包含于数传弧段，则将该窗口+1
//            List<TrbutDataRpcDTO> scTrbutData = trbutDataRpcService.selTrButData(scStationVo);
//            if (!ListUtils.isNoE(scTrbutData)) {
//                for (TrbutDataRpcDTO data : scTrbutData) {
//                    if (DateUtils.DateCaseToLocalDateTime(data.getReceiveStartTime()).isBefore(Objects.requireNonNull(DateUtils.DateCaseToLocalDateTime(kjckDTO.getStartTime())).plusSeconds(1))
//                            && DateUtils.DateCaseToLocalDateTime(data.getReceiveEndTime()).isAfter(Objects.requireNonNull(DateUtils.DateCaseToLocalDateTime(kjckDTO.getEndTime())).minusSeconds(1))) {
//                        implementScore = 1;
//                        chVO.setTrbutData(data);
//                        break;
//                    }
//                }
//            }
//
//            // 成像质量打分
//            float imagingQuality;
//            switch (kjckDTO.getType()) {
//                case TypeConstants.CCD: {
//                    float quaResu;
//                    float resolution = kjckDTO.getGroundResolution() > 50
//                            ? 50 : kjckDTO.getGroundResolution();
//                    if (resolution <= 2) {
//                        quaResu = 0.8f +
//                                0.2f * (2 - kjckDTO.getGroundResolution()) / 2;
//                    } else {
//                        quaResu = 0.5f * (50 - resolution) / 50;
//                    }
//                    float quaSun;
//                    if (kjckDTO.getSunAngle() >= 50) {
//                        quaSun = 0.8f + 0.2f * (kjckDTO.getSunAngle() - 50f) / 50f;
//                    } else {
//                        quaSun = 0.6f * kjckDTO.getSunAngle() / 50;
//                    }
//                    float quaSway;
//                    if (kjckDTO.getRollAngle() <= 10) {
//                        quaSway = 0.85f + 0.15f * (10 - kjckDTO.getRollAngle()) / 10;
//                    } else {
//                        quaSway = 0.6f * (65f - kjckDTO.getRollAngle()) / 65f;
//                    }
//                    imagingQuality = 0.4f * quaResu + 0.25f + 0.25f * quaSun
//                            + 0.1f * quaSway;
//                    break;
//                }
//                case TypeConstants.SAR: {
//                    float quaResu;
//                    if (kjckDTO.getGroundResolution() <= 2) {
//                        quaResu = 0.8f + 0.2f
//                                * (2f - kjckDTO.getGroundResolution()) / 2f;
//                    } else {
//                        quaResu = 0.5f * (50f - kjckDTO.getGroundResolution()) / 50f;
//                    }
//                    float quaSway;
//                    if (kjckDTO.getRollAngle() <= 20) {
//                        quaSway = 0.9f + 0.1f * (20f - kjckDTO.getRollAngle()) / 10;
//                    } else {
//                        quaSway = 0.6f * (65f - kjckDTO.getRollAngle()) / 65f;
//                    }
//                    imagingQuality = 0.7f * quaResu + 0.3f * quaSway;
//                    break;
//                }
//                default:
//                    imagingQuality = 0f;
//                    break;
//            }
//            // 时效性打分
//            float timeliness;
//            if (Objects.requireNonNull(DateUtils.DateCaseToLocalDateTime(kjckDTO.getStartTime()))
//                    .isBefore(Objects.requireNonNull(DateUtils.DateCaseToLocalDateTime(demandDTO.getStartTime())).plusMinutes(30))) {
//                timeliness = 1;
//            } else if (kjckDTO.getStartTime().equals(demandDTO.getEndTime())) {
//                timeliness = 0;
//            } else {
//                Duration ckStartStepStart = Duration.between(Objects.requireNonNull(DateUtils.DateCaseToLocalDateTime(demandDTO.getStartTime())), DateUtils.DateCaseToLocalDateTime(kjckDTO.getStartTime()));
//                Duration stepEndStepStart = Duration.between(Objects.requireNonNull(DateUtils.DateCaseToLocalDateTime(demandDTO.getEndTime())), DateUtils.DateCaseToLocalDateTime(demandDTO.getStartTime()));
//                timeliness = 1f - (ckStartStepStart.toMinutes() - 30f) / (stepEndStepStart.toMinutes() - 30f);
//            }
//            //  该窗口所得分值
//            float socre = divisorVo.getA() * implementScore + divisorVo.getB() * imagingQuality + divisorVo.getY() * timeliness;
//            QueryScoreKjckDTO kjckScoreVo = new QueryScoreKjckDTO();
//            BeanUtils.copyProperties(kjckDTO, kjckScoreVo);
//            kjckScoreVo.setScore(socre);
//            list.add(kjckScoreVo);
//        }
//        chVO.setQueryScoreKjckDTOs(list);
//        return chVO;
//    }


    /**
     * 如果优先级小于40
     *
     * @param
     * @return
     */
//    public boolean vaildPriority(QueryDemandDTO demandDTO) {
//        boolean isSuccess = false;
//        //  取出该需求下的所有可见窗口
//        List<QueryKjckDTO> kjckDTOList = demandDTO.getKjckDTOList();
//        for (QueryKjckDTO kjckDTO : kjckDTOList) {
//            //  查询数传弧段(访问时间作为开始时间，需求结束时间)
//            CxStationRpcDTO scStationVo = CxStationRpcDTO
//                    .builder()
//                    .satelliteId(kjckDTO.getSatelliteId())
//                    .cxStartTime(kjckDTO.getVisitDateTime())
//                    .cxEndTime(demandDTO.getEndTime())
//                    .build();
//
//            //  如果能查询出结果，说明该卫星访问时间段是包含于数传弧段，则该窗口满足时效性
//            List<TrbutDataRpcDTO> scTrbutData = trbutDataRpcService.selTrButData(scStationVo);
//            if (!ListUtils.isNoE(scTrbutData)) {
//                for (TrbutDataRpcDTO data : scTrbutData) {
//                    if (data.getReceiveStartTime().isBefore(Objects.requireNonNull(DateUtils.DateCaseToLocalDateTime(kjckDTO.getStartTime())).plusSeconds(1))
//                            && data.getReceiveEndTime().isAfter(Objects.requireNonNull(DateUtils.DateCaseToLocalDateTime(kjckDTO.getEndTime())).minusSeconds(1))) {
//                        isSuccess = true;
//                        break;
//                    }
//                }
//            }
//        }
//        return isSuccess;
//    }
    private List<QueryKjckDTO> getKjckDTOList(List<AlgSchemeStepWindow> stepWindows, SatelliteIdAndPayloadDTO satelliteIdAndPayloadDTO) {
        List<QueryKjckDTO> list = new ArrayList<>();
        //查询条带，后面太阳角高度需要用到（电子星是没有太阳角高度的）
//        List<String> windowIds = stepWindows.stream().map(AlgSchemeStepWindow::getId).collect(Collectors.toList());
//        if (ListUtils.isNoE(windowIds)) {
//            throw new NullPointerException("没有适配的可见窗口id集合");
//        }
//        List<BizTimeWindow> bizTimeWindowList = bizTimeWindowService.getListByWindowIds(windowIds);
//        if (ListUtils.isNoE(bizTimeWindowList)) {
//            throw new NullPointerException("根据可见窗口id未找到条带信息");
//        }
        for (AlgSchemeStepWindow algSchemeStepWindow : stepWindows) {
            QueryKjckDTO queryKjckDTO = new QueryKjckDTO();
            queryKjckDTO.setWindowId(algSchemeStepWindow.getId());
            queryKjckDTO.setSatelliteId(algSchemeStepWindow.getSatelliteId());
            queryKjckDTO.setSatelliteCode(algSchemeStepWindow.getSatelliteCode());
            queryKjckDTO.setStartTime(algSchemeStepWindow.getStartTime());
            queryKjckDTO.setEndTime(algSchemeStepWindow.getEndTime());
            //  卫星成像窗口可见时间
            queryKjckDTO.setTimeInterVal(DateUtils.trByDate(algSchemeStepWindow.getStartTime(), algSchemeStepWindow.getEndTime()));
            queryKjckDTO.setVisitDateTime(algSchemeStepWindow.getAccessTime());
            if (Objects.isNull(algSchemeStepWindow.getResolvingPower())) {
                queryKjckDTO.setGroundResolution(0f);
            } else {
                queryKjckDTO.setGroundResolution(algSchemeStepWindow.getResolvingPower().floatValue());
            }
            queryKjckDTO.setType(satelliteIdAndPayloadDTO.getType());
            //  todo 原有代码对条带入库区别判断，电子星条带入election_strip_info 光学星\sar条带入biz_time_window。后续条带入库调整不同的载荷统一入库strip_info表
//            queryKjckDTO.setSunAngle(Float.parseFloat(bizTimeWindowList.stream().filter(e -> e.getTimeWindowId().equals(algSchemeStepWindow.getId())).findFirst().get().getSunAngle()));
            queryKjckDTO.setPitchAngle(algSchemeStepWindow.getPitchAngle());
            queryKjckDTO.setRollAngle(algSchemeStepWindow.getSideSwayValue().floatValue());
            queryKjckDTO.setSunAngle(Float.parseFloat(Double.toString(algSchemeStepWindow.getSunAngle())));
            list.add(queryKjckDTO);
        }
        return list;
    }

    /**
     * 根据parentId进行筹划方案生成
     * 思路：
     * 1.其中alg_scheme_step_window表中存放条带的窗口数据，按照窗口时间从小到大排序，
     * 2.通过satelliteId来对排序后的窗口数据进行分组，以Map<String,List<AlgSchemeStepWindow>>结构存储
     * 3.将Map<String,List<AlgSchemeStepWindow>>按照List<AlgSchemeStepWindow>大小排序
     * 4.遍历Map<String,List<AlgSchemeStepWindow>>集合，优先找出不同satelliteId的时间不重合的目标条带窗口。并记录到selectedRecords中。
     *
     * @param demandParam 筹划方案请求参数类
     * @return 筹划方案生成结果
     */
    public AjaxResult planAllTarget(DemandParam demandParam) {
        List<AlgSchemeStepWindow> algSchemeStepWindowList;
        List<AlgSchemeStepWindow> selectedRecords = new ArrayList<>();
        Set<String> timeRanges = new HashSet<>();
        Set<String> selectedParentIds = new HashSet<>();

        if(demandParam.getDemandType().equals(DEMAND_TYPE_FULL)){
            //1.1 根据第一条parent_id模糊查询alg_scheme_step_window中parent_id前三部分一致的数据，然后按照卫星冲突检测规则进行时间筛选
            String parentIdSub = demandParam.getParentIds().get(0);
            String parentIdSubstring = parentIdSub.substring(0, parentIdSub.lastIndexOf("_"));
            algSchemeStepWindowList = algSchemeStepWindowService.getAlgSchemeStepWindowByParentIdSubstring(parentIdSubstring);
        }else{
            //2.1 根据传入的parent_id集合查询alg_scheme_step_window中parent_id一致的数据，然后按照卫星冲突检测规则进行时间筛选
            algSchemeStepWindowList = algSchemeStepWindowService.getAlgSchemeStepWindowByParentIds(demandParam.getParentIds());
        }
        //这里实现目标个数的统计
        long targetParentIdCount= algSchemeStepWindowList.stream().map(AlgSchemeStepWindow::getParentId).distinct().count();
        selectedRecords = findFilteredData(algSchemeStepWindowList,targetParentIdCount);
        log.info("筹划方案生成结果条数：{}", selectedRecords.size());
        //目标对应条带窗口已经筛选完成，这里开始处理对应条带与数传站
        //筛选拍摄目标条带的satelliteId
        List<String> windowIds = selectedRecords.stream().map(AlgSchemeStepWindow::getId).collect(Collectors.toList());
        // 根据windowIds查询alg_scheme_window_pass表，获取对应的数传站与测控站资源
        List<AlgSchemeWindowPass> algSchemeWindowPassesByWindowIds = algSchemeWindowPassMapper.selectSchemeWindowPassByWindowIds(windowIds);
        //筛选测控站数据
        List<AlgSchemeWindowPass> controlWindowPass = algSchemeWindowPassesByWindowIds.stream().filter(e -> e.getPassType()==PassTypeEnum.DEMAND_CK.getCode()).collect(Collectors.toList());
        //筛选数传站数据
        List<AlgSchemeWindowPass> dataTransferWindowPass = algSchemeWindowPassesByWindowIds.stream().filter(e -> e.getPassType()==PassTypeEnum.DEMAND_SC.getCode()).collect(Collectors.toList());
        //处理选择的条带窗口数据
        Map<String,AlgSchemeStepWindow> stepWindowsMap = handleSelectedRecords(selectedRecords);
        //处理测控站数据
        List<StepWindowControlPassRelation> stepWindowControlPassRelations = handleControlWindowPassData(stepWindowsMap, controlWindowPass);
        //处理数传站数据
        List<StepWindowDataPassRelation> stepWindowDataPassRelations = handleDataTransferWindowPassData(stepWindowsMap, dataTransferWindowPass);
        //进行数据修改
        return AjaxResult.success(windowIds);
    }

    private Map<String, AlgSchemeStepWindow> handleSelectedRecords(List<AlgSchemeStepWindow> selectedRecords) {
        return selectedRecords.stream()
                .collect(Collectors.toMap(AlgSchemeStepWindow::getId, Function.identity()));
    }

    /**
     * 处理数传站数据
     * @param selectedRecordsMap:已选择窗口列表
     * @param dataTransferWindowPass:数传站数据
     * @return 选择的数传站集合
     */
    private List<StepWindowDataPassRelation> handleDataTransferWindowPassData(Map<String,AlgSchemeStepWindow> selectedRecordsMap, List<AlgSchemeWindowPass> dataTransferWindowPass) {
        List<StepWindowDataPassRelation> StepWindowDataPassRelations = new ArrayList<>();

        //将数传站集合转换为Map<String,AlgSchemeWindowPass>的结构
        Map<String, List<AlgSchemeWindowPass>> dataTransferWindowPassMap = dataTransferWindowPass.stream()
                .collect(Collectors.groupingBy(AlgSchemeWindowPass::getSchemeStepWindowId));

        for(Map.Entry<String, AlgSchemeStepWindow> algSchemeStepWindowEntry : selectedRecordsMap.entrySet()){
            String windowId = algSchemeStepWindowEntry.getKey();
            AlgSchemeStepWindow algSchemeStepWindow = algSchemeStepWindowEntry.getValue();

            List<AlgSchemeWindowPass> algSchemeWindowPasses = dataTransferWindowPassMap.get(windowId);

            //获取条带窗口结束时间
            Date windowEndTime = algSchemeStepWindow.getEndTime();
            //查询距离条带窗口结束时间最近的数传站
            Optional<AlgSchemeWindowPass> minTransferStationPass = algSchemeWindowPasses.stream()
                    .min(Comparator.comparing(obj -> Math.abs(obj.getStartTime().getTime() - windowEndTime.getTime())));
            if(minTransferStationPass.isPresent()){
                AlgSchemeWindowPass algSchemeWindowPass = minTransferStationPass.get();
                StepWindowDataPassRelation stepWindowDataPassRelation = new StepWindowDataPassRelation();
                stepWindowDataPassRelation.setStepId(algSchemeStepWindow.getSchemeStepId());
                stepWindowDataPassRelation.setDataStationWindowId(algSchemeWindowPass.getId());
                stepWindowDataPassRelation.setWindowId(algSchemeWindowPass.getSchemeStepWindowId());
                stepWindowDataPassRelation.setIsConfirm(IS_CONFIRMED);
                StepWindowDataPassRelations.add(stepWindowDataPassRelation);
            }
        }

        return StepWindowDataPassRelations;
    }

    /**
     * 处理测控站数据
     * @param selectedRecordsMap:已选择窗口列表
     * @param controlWindowPass:测控站数据
     * @return 选择的测控站集合
     */
    private List<StepWindowControlPassRelation> handleControlWindowPassData(Map<String,AlgSchemeStepWindow> selectedRecordsMap, List<AlgSchemeWindowPass> controlWindowPass) {
        List<StepWindowControlPassRelation> controlPassRelations = new ArrayList<>();

        //根据窗口id分组
        Map<String, List<AlgSchemeWindowPass>> controlWindowPassMap = controlWindowPass.stream()
                .collect(Collectors.groupingBy(AlgSchemeWindowPass::getSchemeStepWindowId));

        for(Map.Entry<String, AlgSchemeStepWindow> algSchemeStepWindowEntry : selectedRecordsMap.entrySet()){

            String algSchemeStepWindowId = algSchemeStepWindowEntry.getKey();
            AlgSchemeStepWindow algSchemeStepWindow = algSchemeStepWindowEntry.getValue();

            //获取条带窗口访问时间
            Date accessTime = algSchemeStepWindow.getAccessTime();
            // 查询距离条带访问最远的测控站弧度
            List<AlgSchemeWindowPass> algSchemeWindowPasses = controlWindowPassMap.get(algSchemeStepWindowId);
            Optional<AlgSchemeWindowPass> maxControlWindowPass = algSchemeWindowPasses.stream()
                    .max(Comparator.comparing(obj -> Math.abs(accessTime.getTime() - obj.getEndTime().getTime())));
            if(maxControlWindowPass.isPresent()){
                AlgSchemeWindowPass algSchemeWindowPass = maxControlWindowPass.get();
                StepWindowControlPassRelation stepWindowDataPassRelation = new StepWindowControlPassRelation();
                stepWindowDataPassRelation.setWindowId(algSchemeWindowPass.getSchemeStepWindowId());
                stepWindowDataPassRelation.setStepId(algSchemeStepWindow.getSchemeStepId());
                stepWindowDataPassRelation.setControlStationWindowId(algSchemeWindowPass.getId());
                stepWindowDataPassRelation.setIsConfirm(IS_CONFIRMED);
                controlPassRelations.add(stepWindowDataPassRelation);
            }

        }
        return controlPassRelations;
    }

    /**
     * 处理algSchemeStepWindowList
     * @param algSchemeStepWindowList:待处理的条带窗口数据
     * @param timeRanges:已选择时间段
     * @param selectedRecords:以选择窗口列表
     */
    private void handleAlgSchemeStepWindowList(List<AlgSchemeStepWindow> algSchemeStepWindowList, Set<String> timeRanges, List<AlgSchemeStepWindow> selectedRecords, Set<String> selectedParentIds, long targetParentIdCount) {
        for (AlgSchemeStepWindow record : algSchemeStepWindowList) {
            Timestamp startTime = new Timestamp(record.getStartTime().getTime());
            Timestamp endTime = new Timestamp(record.getEndTime().getTime());
            String parentId = record.getParentId();
            String satelliteId = record.getSatelliteId();

            String rangeKey = startTime + "_" + endTime;

            // 确保 parent_id 不重复
            if (selectedParentIds.contains(parentId)) {
                continue;
            }

            // 检查时间段是否重合
            boolean overlaps = timeRanges.stream().anyMatch(existingRange ->
                    isOverlapping(existingRange, startTime, endTime));

            if (!overlaps) {
                selectedRecords.add(record);
                timeRanges.add(rangeKey);

                if (selectedRecords.size() >= targetParentIdCount) {
                    break;
                }
            }
        }
    }

    private boolean isOverlapping(String existingRange, Date startTime, Date endTime) {
        String[] parts = existingRange.split("_");
        Timestamp existingStart = Timestamp.valueOf(parts[0]);
        Timestamp existingEnd = Timestamp.valueOf(parts[1]);

        return !(endTime.before(existingStart));
    }


    public List<AlgSchemeStepWindow> findFilteredData(List<AlgSchemeStepWindow> dataList, long limit) {
        // 通过satelliteId对窗口进行分类
        Map<String, List<AlgSchemeStepWindow>> groupedBySatelliteId = dataList.stream()
                .collect(Collectors.groupingBy(AlgSchemeStepWindow::getSatelliteId));

        //对分类完成的map按照Value值(List)的大小进行排序
        Map<String, List<AlgSchemeStepWindow>> algSchemeStepWindowSortMap = groupedBySatelliteId.entrySet().stream()
                .sorted(Comparator.comparingInt(entry -> entry.getValue().size()))
                .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    Map.Entry::getValue,
                    (oldValue, newValue) -> oldValue,
                    LinkedHashMap::new
                ));

        Set<String> usedParentIds = new HashSet<>();
        List<AlgSchemeStepWindow> selectedWindowList = new ArrayList<>();
        //设置一个数，避免死循环
        int loopCount = 0;

        while (selectedWindowList.size() < limit) {
            for (Map.Entry<String, List<AlgSchemeStepWindow>> entry : algSchemeStepWindowSortMap.entrySet()) {
                String satelliteId = entry.getKey();
                List<AlgSchemeStepWindow> satelliteData = entry.getValue();

                satelliteData.sort(Comparator.comparing(AlgSchemeStepWindow::getStartTime).reversed());

                List<AlgSchemeStepWindow> nonOverlappingRecords = new ArrayList<>();

                for (AlgSchemeStepWindow record : satelliteData) {
                    if (usedParentIds.contains(record.getParentId())) {
                        continue;
                    }

                    if (!isOverlapping(nonOverlappingRecords, record)) {
                        nonOverlappingRecords.add(record);
                        usedParentIds.add(record.getParentId());
                        selectedWindowList.add(record);

                        if (selectedWindowList.size() >= limit) {
                            return selectedWindowList;
                        }
                        break;
                    }
                }
            }

            // 这里防止产生死循环
            loopCount++;
            if (loopCount > 2) {
                break;
            }
        }

        return selectedWindowList;
    }

    private static boolean isOverlapping(List<AlgSchemeStepWindow> records, AlgSchemeStepWindow newRecord) {
        for (AlgSchemeStepWindow record : records) {
            if (!(newRecord.getEndTime().before(record.getStartTime()))) {
                return true;
            }
        }
        return false;
    }
}
