package com.aps.service.Algorithm;

import com.aps.common.util.FileHelper;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.*;
import com.aps.entity.basic.*;
import com.aps.service.plan.MachineSchedulerService;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * 作者：佟礼
 * 时间：2026-04-28
 */
public class GeneticDecoderBom    {

    private MachineCalculator machineCalculator;

    private MaterialRequirementService materialRequirementService;

    private LocalDateTime baseTime;

    GeneticDecoderBom(LocalDateTime _baseTime,MachineCalculator _machineCalculator,MaterialRequirementService _materialRequirementService)
    {
        baseTime=_baseTime;
        machineCalculator=_machineCalculator;
        materialRequirementService=_materialRequirementService;
    }

    public int computeSemiFinishedAnchor(GeneticDecoder decoder,int semiGroupId,
                                         Map<Integer, List<Entry>> entrysBygroupId,
                                         Map<String, OpMachine> opMachineKeyMap,
                                         Chromosome chromosome,
                                         Map<Integer, GAScheduleResult> scheduleIndexById,
                                         Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache
            ,Map<Long, Machine> machineIdMap,Map<Integer, Entry> entryIndexById,
                                         boolean isCheckMp,
                                         Map<Integer, Integer> targetGroupEnd) {
        List<Entry> semiOps = entrysBygroupId.get(semiGroupId);
        if (semiOps == null || semiOps.isEmpty()) return 0;


        int currentGroupId = semiGroupId;
        List<Integer> intermediateGroupIds = new ArrayList<>();
        intermediateGroupIds.add(semiGroupId);
       boolean isFORWARD=false;
        while (true) {
            List<Entry> ops = entrysBygroupId.get(currentGroupId);
            Entry lastOp = ops.stream()
                    .max(Comparator.comparingInt(Entry::getSequence)).orElse(null);
            if (lastOp == null || lastOp.getTargetFinishedOperationId() == null
                    || lastOp.getTargetFinishedOperationId().isEmpty()) break;
            int targetOpId = lastOp.getTargetFinishedOperationId().get(0);
            int targetGroupId = findGroupForOperation(targetOpId, entrysBygroupId);

            if (targetGroupId < 0) break;
            List<Entry> targetOps = entrysBygroupId.get(targetGroupId);
            if (targetOps == null || targetOps.isEmpty()) break;
            Entry firstTargetOp = targetOps.get(0);

            //半成品对应的成品工序
            Entry targetOp = entryIndexById.get(targetOpId);
            //成品工序下所有半成品订单
            List<Integer> simGroupIds = targetOp != null ? targetOp.getDependentOnOrderIds() : null;
           //是否头多半成品
            boolean hasMultipleSemi = simGroupIds != null && simGroupIds.size() > 1 ;


            if (Entry.SchedulingMode.FORWARD.name().equals(
                    firstTargetOp.getSchedulingMode())) {

                int rootStart = forwardEstimateToOperation(decoder,targetOpId, targetGroupId,0,
                        entrysBygroupId, opMachineKeyMap, chromosome,
                        scheduleIndexById, machineTasksCache,machineIdMap,entryIndexById,false,null);
                List<Integer> chainGroupIds = new ArrayList<>(intermediateGroupIds);
                chainGroupIds.add(targetGroupId);

                rootStart = correctByRawMaterialStockForChain(chainGroupIds,targetGroupId,targetOpId,
                        rootStart, entrysBygroupId,chromosome,isCheckMp);
                int anchor = rootStart;

              //  FileHelper.writeLogFile("  成品开始    "+targetGroupId+" : "+rootStart);

                if(!hasMultipleSemi) {
                    if(anchor==0)
                    {
                        return -1;
                    }
                    for (int i = intermediateGroupIds.size() - 1; i >= 0; i--) {

                        int sfGroupId = intermediateGroupIds.get(i);
                        int start1 = forwardEstimateToOperation(decoder, 0, sfGroupId, anchor,
                                entrysBygroupId, opMachineKeyMap, chromosome,
                                scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById, true,null);

                        // FileHelper.writeLogFile("  半成品开始    "+sfGroupId+" : "+start1+" - "+anchor);


                        if (start1 <= 0) {
                            isFORWARD = true;
                            break;
                        }

                        if (i != 0) {
                            anchor -= start1;
                            if (anchor < 0) {
                                isFORWARD = true;
                                break;
                            }
                        } else {
                            if (anchor - start1 < 0) {
                                isFORWARD = true;
                                break;
                            }
                        }
                    }

                    if (isFORWARD) {
                        for (int i = intermediateGroupIds.size() - 1; i >= 0; i--) {

                            List<Entry> ops1 = entrysBygroupId.get(intermediateGroupIds.get(i));
                            ops1.forEach(t -> t.setSchedulingMode(Entry.SchedulingMode.FORWARD.name()));
                        }
                        return -1;
                    }
                    return Math.max(0, anchor);
                }
                else
                {
                    int maxOtherSemiEnd = 0;
                   if(targetGroupEnd!=null&&targetGroupEnd.get(targetGroupId)!=null)
                   {
                       maxOtherSemiEnd=targetGroupEnd.get(targetGroupId);

                   }else {

                       // 多半成品：临时设备占用表，跨组追踪设备时间
                       Map<Long, Integer> tempMachineEnd = new HashMap<>();
                       for (int otherGroupId : simGroupIds) {
                          // if (otherGroupId == semiGroupId) continue;

                           Set<Integer> visited = new HashSet<>();
                           int chainEnd = estimateChainEnd(decoder, otherGroupId, rootStart,
                                   entrysBygroupId, opMachineKeyMap, chromosome,
                                   scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById,
                                   tempMachineEnd, visited);
                           if (chainEnd > maxOtherSemiEnd) {
                               maxOtherSemiEnd = chainEnd;
                           }
                       }

                       targetGroupEnd.put(targetGroupId, maxOtherSemiEnd);
                   }
                    anchor = Math.max(anchor, maxOtherSemiEnd);
                    for (int i = intermediateGroupIds.size() - 1; i >= 0; i--) {
                        int sfGroupId = intermediateGroupIds.get(i);
                        int start1 = forwardEstimateToOperation(decoder, 0, sfGroupId, anchor,
                                entrysBygroupId, opMachineKeyMap, chromosome,
                                scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById, true,null);
                        if (start1 <= 0) {
                            isFORWARD = true;
                            break;
                        }
                        if (i != 0) {
                            anchor -= start1;
                            if (anchor < 0) {
                                isFORWARD = true;
                                break;
                            }
                        } else {
                            if (anchor - start1 < 0) {
                                isFORWARD = true;
                                break;
                            }
                        }
                    }

                    if (isFORWARD) {
                        // 倒排失败，尝试延迟对齐
                        int effectiveAnchor = Math.max(rootStart, maxOtherSemiEnd);
                        int forwardEnd = forwardEstimateToOperation(decoder, 0, semiGroupId, 0,
                                entrysBygroupId, opMachineKeyMap, chromosome,
                                scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById, false,null);
                        if (forwardEnd > 0 && forwardEnd <= effectiveAnchor) {
                            return effectiveAnchor;
                        }
                        for (int i = intermediateGroupIds.size() - 1; i >= 0; i--) {
                            List<Entry> ops1 = entrysBygroupId.get(intermediateGroupIds.get(i));
                            ops1.forEach(t -> t.setSchedulingMode(Entry.SchedulingMode.FORWARD.name()));
                        }
                        return -1;
                    }

                }

                return Math.max(0, anchor);
            }
            intermediateGroupIds.add(targetGroupId);
            currentGroupId = targetGroupId;
        }
        return 0;
    }

    /**
     * 递归估算一个组及其所有子依赖链的设备占用时间
     * 通过 getDependentOnOrderIds() 找到该组工序依赖的下游半成品组，
     * 从最深层的子半成品（先跑）到当前组（后跑），
     * 深层组的设备时间累积到 tempMachineEnd，浅层组自然排队
     *
     * @param groupId 起始组ID
     * @param tempMachineEnd 设备占用表（会被更新）
     * @param visited 已访问组集合，防止循环依赖
     * @return 该组链的最大结束时间
     */
    /**
     * 递归估算一个组及其所有子依赖链的设备占用时间
     * 通过 getDependentOnOrderIds() 找到该组工序依赖的下游半成品组，
     * 从最深层的子半成品（先跑）到当前组（后跑），
     * 深层组的设备时间累积到 tempMachineEnd，浅层组自然排队
     *
     * @param groupId 起始组ID
     * @param backwardAnchor 倒排目标结束时间，>0时优先尝试倒排，=0则直接正排
     * @param tempMachineEnd 设备占用表（会被更新）
     * @param visited 已访问组集合，防止循环依赖
     * @return 该组链的最大结束时间
     */
    private int estimateChainEnd(GeneticDecoder decoder, int groupId, int backwardAnchor,
                                 Map<Integer, List<Entry>> entrysBygroupId,
                                 Map<String, OpMachine> opMachineKeyMap,
                                 Chromosome chromosome,
                                 Map<Integer, GAScheduleResult> scheduleIndexById,
                                 Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,
                                 Map<Long, Machine> machineIdMap,
                                 Map<Integer, Entry> entryIndexById,
                                 Map<Long, Integer> tempMachineEnd,
                                 Set<Integer> visited) {
        if (!visited.add(groupId)) return 0; // 防止循环依赖

        List<Entry> ops = entrysBygroupId.get(groupId);
        if (ops == null || ops.isEmpty()) return 0;

        // 先递归估算该组依赖的子半成品（子依赖始终正排，找到最小必需结束时间）
        int subMaxEnd = 0;
        for (Entry op : ops) {
            List<Integer> depGroupIds = op.getDependentOnOrderIds();
            if (depGroupIds != null) {
                for (int depGroupId : depGroupIds) {
                    if (depGroupId != groupId) {
                        int subEnd = estimateChainEnd(decoder, depGroupId, 0,
                                entrysBygroupId, opMachineKeyMap, chromosome,
                                scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById,
                                tempMachineEnd, visited);
                        if (subEnd > subMaxEnd) subMaxEnd = subEnd;
                    }
                }
            }
        }

        // 优先尝试倒排：尽量晚结束，给同层其他半成品留出更大的锚点空间
        if (backwardAnchor > 0) {
            int backwardStart = forwardEstimateToOperation(decoder, 0, groupId, backwardAnchor,
                    entrysBygroupId, opMachineKeyMap, chromosome,
                    scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById,
                    true, tempMachineEnd);
            // 倒排成功（start > 0）且子依赖能在倒排开始前完成
            if (backwardStart > 0 && backwardStart >= subMaxEnd) {
                return backwardAnchor;
            }
        }

        // 倒排失败或未指定锚点：正排，从子依赖结束时间开始（tempMachineEnd + subMaxEnd 双重保障）
        int forwardEnd = forwardEstimateToOperation(decoder, 0, groupId, subMaxEnd,
                entrysBygroupId, opMachineKeyMap, chromosome,
                scheduleIndexById, machineTasksCache, machineIdMap, entryIndexById,
                false, tempMachineEnd);

        return Math.max(forwardEnd, subMaxEnd);
    }

    private int findGroupForOperation(int opId,
                                      Map<Integer, List<Entry>> entrysBygroupId) {
        for (Map.Entry<Integer, List<Entry>> e : entrysBygroupId.entrySet()) {
            for (Entry op : e.getValue()) {
                if (op.getId() == opId) return e.getKey();
            }
        }
        return -1;
    }
    private int forwardEstimateToOperation(GeneticDecoder decoder, int targetOpId, int groupId,int startSecond,
                                           Map<Integer, List<Entry>> entrysBygroupId,
                                           Map<String, OpMachine> opMachineKeyMap,
                                           Chromosome chromosome,
                                           Map<Integer, GAScheduleResult> scheduleIndexById,
                                           Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,
                                           Map<Long, Machine> machineIdMap,
                                           Map<Integer, Entry> entryIndexById,boolean isJit,Map<Long, Integer> tempMachineEnd) {
      if(startSecond<=0&&isJit)
      {
         return -1;
      }
        List<Entry> ops = entrysBygroupId.get(groupId);
        if (ops == null) return 0;

        List<Entry> sorted = new ArrayList<>();

if(isJit)
{
    sorted = ops.stream()
            .sorted(Comparator.comparingInt(Entry::getSequence).reversed())
            .collect(Collectors.toList());
}else {
    sorted = ops.stream()
            .sorted(Comparator.comparingInt(Entry::getSequence))
            .collect(Collectors.toList());
}

        Map<Long, Machine> machineById = chromosome.getMachines().stream()
                .collect(Collectors.toMap(Machine::getId, m -> m, (a, b) -> a));
        int nextAvailableStart = startSecond;
        Map<Long, Integer> machineLastEndInOrder = new HashMap<>();
        boolean useTempMachineEnd = tempMachineEnd != null;

        for (Entry op : sorted) {
            String key = groupId + "_" + op.getSequence();
            OpMachine mo = opMachineKeyMap.get(key);
            if (mo == null) continue;
            Long mId = mo.getMachineId();
            Machine machine = machineById.get(mId);

            int start = nextAvailableStart;
            int end=start;
            // 该工序已排产 → 直接用真实结束时间作为后续工序的基准
            GAScheduleResult prevResult = scheduleIndexById != null
                    ? scheduleIndexById.get(op.getId()) : null;
            if (prevResult != null) {
                int scheduledEnd = prevResult.getEndTime();
                start = Math.max(start, scheduledEnd);
                if (op.getId() == targetOpId) return prevResult.getStartTime();
                machineLastEndInOrder.put(mId, scheduledEnd);
                nextAvailableStart = Math.max(nextAvailableStart, scheduledEnd);
                continue;
            }

            if(!isJit) {
                // 同一订单内该设备已有工序的结束时间
                Integer lastInOrder = machineLastEndInOrder.get(mId);
                if (lastInOrder != null) start = Math.max(start, lastInOrder);

                // 临时设备占用表：跨组追踪设备占用
                if (useTempMachineEnd) {
                    Integer lastEnd = tempMachineEnd.get(mId);
                    if (lastEnd != null) {
                        start = Math.max(start, lastEnd);
                    }
                }

                // 该设备上已排产的其他任务（半成品占位）的结束时间
                if (!useTempMachineEnd && machineTasksCache != null) {
                    CopyOnWriteArrayList<GAScheduleResult> tasks = machineTasksCache.get(mId);
                    if (tasks != null) {
                        for (GAScheduleResult t : tasks) {
                            start = Math.max(start, t.getEndTime());
                        }
                    }
                }
            }

            int rawDur=0;
            if(op.getConstTime()==1||machine.getCapacityTypeName().equals("Infinite"))
            {
                if(op.getQuantity()==0)
                {
                    rawDur=0;
                }else {

                    rawDur = (int) Math.ceil(mo.getProcessingTime());
                }
            }else {
                rawDur = (int) Math.ceil(mo.getRuntime().doubleValue()
                        / mo.getSingleOut().doubleValue() * op.getQuantity());
            }


             if (machine != null&&!machine.getCapacityTypeName().equals("Infinite")) {
                CopyOnWriteArrayList<GAScheduleResult> machineTasks = getMachineTasks(mId, chromosome, machineTasksCache);



                GAScheduleResult result= decoder.processOperationResult( op, mId, mo.getProcessingTime(), mo, chromosome, machineIdMap, machineTasksCache, entryIndexById, scheduleIndexById, start, false,  isJit);


              //  CopyOnWriteArrayList<ScheduleResultDetail> geneDetails = estimateCalendarAwareDuration(machine,op,machineTasks, start, rawDur,mo.getProcessingTime(),isJit);
              if(result!=null) {
                  start = result.getStartTime()-result.getChangeOverTime();
                  end = result.getEndTime();
                  if (useTempMachineEnd) {
                      tempMachineEnd.put(mId, end);
                  }
              }else {
                  return -1;
              }


            }else {



                if(isJit)
                {
                    end=start;
                    start=start-rawDur;

                }else {
                    start=end;
                    end=start+rawDur;
                }
              //  FileHelper.writeLogFile("  半成品    " + op.getGroupId() + " - " + op.getSequence() + ",开始时间: " + start + ",结束时间: " + end + ",处理时间: " + rawDur );

            }

            if (op.getId() == targetOpId) return start;


            machineLastEndInOrder.put(mId, end);
            if(isJit)
            {
                nextAvailableStart = Math.min(nextAvailableStart, start);
            }else {
                nextAvailableStart = Math.max(nextAvailableStart, end);
            }
        }

        // targetOpId 不在该订单工序内：返回前序工序全部处理后的最早可用开工时间
        return nextAvailableStart;
    }

    private CopyOnWriteArrayList<ScheduleResultDetail> estimateCalendarAwareDuration(Machine machine,Entry operation,CopyOnWriteArrayList<GAScheduleResult> machineTasks, int startSecond,
                                              int rawDurationSecond,double  processingTime,boolean isJit) {
        CopyOnWriteArrayList<ScheduleResultDetail> geneDetails=new CopyOnWriteArrayList<>();

        geneDetails=  machineCalculator.getNextAvailableTime(machine, operation, startSecond, -1,
                rawDurationSecond, machineTasks, operation.getIsInterrupt()!=1, true,processingTime, operation.getQuantity(), false,isJit);

        return geneDetails;
    }

    public CopyOnWriteArrayList<GAScheduleResult> getMachineTasks(long machineId, Chromosome chromosome,
                                                                  Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache) {
        if (machineTasksCache.containsKey(machineId)) {
            return machineTasksCache.get(machineId);
        }
        CopyOnWriteArrayList<GAScheduleResult> machineTasks = chromosome.getResult().stream()
                .filter(t -> t.getMachineId() == machineId)
                .sorted(Comparator.comparingInt(GAScheduleResult::getStartTime))
                .collect(Collectors.toCollection(CopyOnWriteArrayList::new));
        machineTasksCache.put(machineId, machineTasks);
        return machineTasks;
    }
    private int correctByRawMaterialStockForChain(List<Integer> chainGroupIds,int pfGroupId,int pfOpId,
                                                  int estimatedStartTime,
                                                  Map<Integer, List<Entry>> entrysBygroupId,
                                                  Chromosome chromosome,boolean isCheckMp) {

        TreeMap<String, Material> materials = chromosome.getMaterials();
        if (materials == null || materials.isEmpty()) return estimatedStartTime;
        List<OrderMaterialRequirement> remove=new ArrayList<>();

        Map<String, OrderMaterialRequirement> totalNeededByMaterial = new HashMap<>();


            List<Entry> pfops = entrysBygroupId.get(pfGroupId);

            for (Entry op : pfops) {
                if (op.getMaterialRequirements() == null) continue;
                if(pfOpId!=op.getId())
                {

                    continue;
                }
                for (OrderMaterialRequirement req : op.getMaterialRequirements()) {
                    if (!"MP".equals(req.getMaterialTypeName())&&req.getProductOrderID()!=null&&req.getProductOrderID().size()>0) {
                        OrderMaterialRequirement reqq = totalNeededByMaterial.get(req.getMaterialId());
                        if (reqq != null) {
                            double allneeded = req.getSpentQty() / req.getMainQty() * op.getQuantity();
                            allneeded = reqq.getRequiredQuantity() + allneeded;
                            reqq.setRequiredQuantity(allneeded);
                            reqq.setQjQty(allneeded);
                        } else {
                            OrderMaterialRequirement reqq1 = ProductionDeepCopyUtil.deepCopy(req);

                            double allneeded = req.getSpentQty() / req.getMainQty() * op.getQuantity();

                            reqq1.setRequiredQuantity(allneeded);
                            reqq1.setQjQty(allneeded);
                            totalNeededByMaterial.put(req.getMaterialId(), reqq1);
                        }
                    }
                }
            }
            if(totalNeededByMaterial!=null&&totalNeededByMaterial.size()>0) {
//结合库存计算半成品生产数量，因为可能会修改生产数量所以和原材料分开
                materialRequirementService.CalBom(chromosome, 0, totalNeededByMaterial, materials, baseTime.plusSeconds(estimatedStartTime), false, remove, entrysBygroupId, true,isCheckMp);
            }
        totalNeededByMaterial.clear();
        for (int groupId : chainGroupIds) {
            List<Entry> ops = entrysBygroupId.get(groupId);
            if (ops == null) continue;
            for (Entry op : ops) {
                if (op.getMaterialRequirements() == null) continue;
                if(pfGroupId==op.getGroupId()&&pfOpId!=op.getId())
                {

                    continue;
                }
                for (OrderMaterialRequirement req : op.getMaterialRequirements()) {


                    if (!"MP".equals(req.getMaterialTypeName())&&req.getProductOrderID()!=null&&req.getProductOrderID().size()>0)
                        continue;

                    OrderMaterialRequirement reqq=  totalNeededByMaterial.get(req.getMaterialId());
                    if(reqq!=null)
                    {
                        double allneeded = req.getSpentQty() / req.getMainQty() * op.getQuantity();
                        allneeded=reqq.getRequiredQuantity()+allneeded;
                        reqq.setRequiredQuantity(allneeded);
                        reqq.setQjQty(allneeded);
                    }else {
                        OrderMaterialRequirement reqq1= ProductionDeepCopyUtil.deepCopy(req);

                        double allneeded = req.getSpentQty() / req.getMainQty() * op.getQuantity();

                        reqq1.setRequiredQuantity(allneeded);
                        reqq1.setQjQty(allneeded);
                        totalNeededByMaterial.put(req.getMaterialId(),reqq1);
                    }
                }
            }
        }


        LocalDateTime  estimatedStartTime1= materialRequirementService.CalBom(chromosome,0, totalNeededByMaterial,materials,baseTime.plusSeconds(estimatedStartTime) ,false,remove,entrysBygroupId,false,isCheckMp);
      int  rawTime = (int) Duration.between(baseTime, estimatedStartTime1).getSeconds();

        return Math.max(estimatedStartTime, rawTime);
    }

    /**
     * 半成品锚点计算（JIT倒排模式：成品先于半成品，直接取成品已排的开始时间）
     * 返回 -1 触发正排+标记成品重排，>=0 作为半成品倒排锚点（半成品最晚完成时间）
     */
    public int computeHybridSemiAnchor(GeneticDecoder decoder,int semiOrderId,
                                        Map<Integer, List<Entry>> entrysBygroupId,
                                        Map<String, OpMachine> opMachineKeyMap,
                                        Chromosome chromosome,
                                        Map<Integer, GAScheduleResult> scheduleIndexById,
                                        Map<Long, CopyOnWriteArrayList<GAScheduleResult>> machineTasksCache,
                                        Map<Long, Machine> machineIdMap,
                                        Map<Integer, Entry> entryIndexById,
                                        Set<Integer> reForwardFinishedOrderIds,GlobalParam _globalParam,Order order,Map<Integer, Integer> targetGroupEnd) {

        List<Entry> sfOps = entrysBygroupId.get(semiOrderId);
        if (sfOps == null || sfOps.isEmpty()) return 0;
        Entry firstSfOp = sfOps.stream().min(Comparator.comparing(Entry::getSequence)).orElse(null);
        if (firstSfOp == null) return 0;

        List<Integer> targetFinishedOpIds = firstSfOp.getTargetFinishedOperationId();
        if (targetFinishedOpIds == null || targetFinishedOpIds.isEmpty()) {
            return 0;
        }
int start=Integer.MAX_VALUE;
        for (Integer targetFinishedOpId :targetFinishedOpIds) {


            Entry finishedFirstOp = entryIndexById.get(targetFinishedOpId);
            int finishedOrderId = finishedFirstOp.getGroupId();
            GAScheduleResult finishedFirstResult = scheduleIndexById.get(finishedFirstOp.getId());
            if (finishedFirstResult == null) {
                // 成品还没排（不应该发生在JIT模式），降级为原有逻辑
                return computeSemiFinishedAnchor(decoder, semiOrderId, entrysBygroupId,
                        opMachineKeyMap, chromosome, scheduleIndexById, machineTasksCache,
                        machineIdMap, entryIndexById,_globalParam.isIsCheckMp(),targetGroupEnd);
            }


            // 成品倒排成功 → 半成品倒排：锚点 = 成品开始时间 - 缓冲
            int semiLatestEndTime = finishedFirstResult.getStartTime();

            int semiTotalTime = 0;
            for (Entry op : sfOps) {
                String key = semiOrderId + "_" + op.getSequence();
                OpMachine opt = opMachineKeyMap.get(key);
                if (opt != null) semiTotalTime += calculateOperationProcessingTime(op, opt, _globalParam);
            }
            Material material = chromosome.getMaterials().get(order.getMaterialId());

            int ckeckLeadTime = 0;
            if (material.getCkeckLeadTime() != null) {
                ckeckLeadTime = (material.getCkeckLeadTime().intValue() * 24 * 60 * 60);
            }

            semiTotalTime += ckeckLeadTime;
            if (semiLatestEndTime - semiTotalTime < 0) {
//                FileHelper.writeLogFile("[HybridSemi] 半成品" + semiOrderId +
                  //      " 倒排开工<0，正排+成品" + finishedOrderId + "重排");
                reForwardFinishedOrderIds.add(finishedOrderId);
                start= Math.min(start, -1);
            }else {

//                FileHelper.writeLogFile("[HybridSemi] 半成品" + semiOrderId +
//                        " 锚点=" + semiLatestEndTime + " (成品" + finishedOrderId +
//                        "开始=" + finishedFirstResult.getStartTime() + ")");
                start = Math.min(start, semiLatestEndTime - ckeckLeadTime);
            }

        }
        return start;
    }
    private int calculateOperationProcessingTime(Entry op, OpMachine machineOption,GlobalParam _globalParam) {
        int total = 0;
        if (op.getConstTime() == 1)//常数时间
        {
            total = (int) Math.ceil(machineOption.getRuntime().doubleValue());
        } else {
            double t = machineOption.getRuntime().doubleValue() / machineOption.getSingleOut().doubleValue() * (op.getQuantity());
            total = (int) Math.ceil(t);
        }
        if (op.getSetupTime() != null) total += op.getSetupTime().intValue();

        total += op.getTeardownTime();
        if ( !_globalParam.is_smoothSetup()) {
            total += op.getPreTime();
        }
        return total;
    }

}
