package com.aps.service.Algorithm;

import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.Algorithm.IDAndChildID.NodeInfo;
import com.aps.entity.basic.Entry;
import com.aps.entity.basic.GlobalParam;
import com.aps.entity.basic.ObjectiveConfig;

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

/**
 * 计算各 KPI 的理论下界（Lower Bound）。
 *
 * <p>Gap = (current - lowerBound) / lowerBound（lowerBound > 0 时）
 * <p>当 lowerBound = 0 时（如 Tardiness），Gap = current（表示偏离 0 的绝对量）
 *
 * <p>各维度的理论下界：
 * <ul>
 *   <li>Makespan（最大完工时间）：DAG 关键路径长度（拓扑序 + 动态规划）</li>
 *   <li>FlowTime（总流程时间）：所有工序加工时间之和（∑ processingTime）</li>
 *   <li>SetupTime（总换型时间）：若换型时间只由产品类型决定则为 0；若必须换型则为换型时间之和</li>
 *   <li>MachineLoad（机器负载均衡标准差）：完美均衡下为 0</li>
 *   <li>Tardiness（总延迟时间）：0（可全部按时交付）</li>
 * </ul>
 *
 * 作者：佟礼
 */
public class KpiLowerBoundCalculator {

    /**
     * 对已解码的 chromosome 计算各维度的理论下界，并存入 chromosome.LowerBoundObjectives。
     *
     * @param chromosome 已完成 decode 的染色体
     * @param globalParam 全局参数（用于确定 objectives 数组的顺序与配置）
     */
    public static void computeAndSetLowerBounds(Chromosome chromosome, GlobalParam globalParam) {
        double[] objectives = chromosome.getObjectives();
        if (objectives == null || objectives.length == 0) return;

        double[] lowerBounds = new double[objectives.length];

        // 按 GlobalParam.objectiveConfigs 的顺序（与 objectives 数组一一对应）
        // 注意：globalParam.objectiveConfigs 已按 level 排序
        List<ObjectiveConfig> configs = globalParam.getObjectiveConfigs();

        for (int i = 0; i < objectives.length && i < configs.size(); i++) {
            ObjectiveConfig config = configs.get(i);
            if (!config.isEnabled()) {
                lowerBounds[i] = 0.0;
                continue;
            }
            String name = config.getName();
            if (GlobalParam.OBJECTIVE_MAKESPAN.equals(name)) {
                lowerBounds[i] = computeMakespanLowerBound(chromosome);
            } else if (GlobalParam.OBJECTIVE_TARDINESS.equals(name)) {
                // 延迟理论上界为 0（全部按时）
                lowerBounds[i] = 0.0;
            } else if (GlobalParam.OBJECTIVE_SETUP_TIME.equals(name)) {
                // 换型时间下界：若换型只由产品类型决定则为 0
                lowerBounds[i] = 0.0;
            } else if (GlobalParam.OBJECTIVE_FLOW_TIME.equals(name)) {
                lowerBounds[i] = computeFlowTimeLowerBound(chromosome);
            } else if (GlobalParam.OBJECTIVE_MACHINE_LOAD.equals(name)) {
                // 负载均衡标准差理论上界为 0
                lowerBounds[i] = 0.0;
            } else {
                lowerBounds[i] = 0.0;
            }
        }

        chromosome.setLowerBoundObjectives(lowerBounds);
    }

    /**
     * 计算 Makespan（最大完工时间）的理论下界 = DAG 关键路径长度。
     *
     * <p>思路：对 job shop 问题，完工时间下界 = 所有工序的最长无冲突调度路径长度，
     * 即在不考虑机器冲突的条件下，从根节点到终点的最长加权和路径（边权重 = 加工时间）。
     * 通过拓扑排序 + 动态规划实现（时间复杂度 O(V+E)）。
     */
    private static double computeMakespanLowerBound(Chromosome chromosome) {
        List<GAScheduleResult> result = chromosome.getResult();
        List<GroupResult> operatRel = chromosome.getOperatRel();
        List<Entry> allOperations = chromosome.getAllOperations();

        if (result == null || result.isEmpty()) return 0.0;

        // ---- 1. 构建 nodeId -> processingTime 映射 ----
        Map<Integer, Double> entryProcessingTime = new HashMap<>();
        if (allOperations != null) {
            for (Entry e : allOperations) {
                entryProcessingTime.put(e.getId(), (double) e.getMinProcessingTime());
            }
        }
        // 如果 allOperations 缺失，从 result 推算（使用 flowTime 作为加工时间近似）
        if (entryProcessingTime.isEmpty()) {
            for (GAScheduleResult r : result) {
                entryProcessingTime.put(r.getOperationId(), Math.max(1.0, r.getProcessingTime()));
            }
        }

        // ---- 2. 构建 DAG：nodeId -> [childNodeIds] ----
        Map<Integer, List<Integer>> dag = new HashMap<>();
        Set<Integer> allNodeIds = new HashSet<>();
        // 初始化所有节点
        for (GAScheduleResult r : result) {
            int eid = r.getOperationId();
            dag.putIfAbsent(eid, new ArrayList<>());
            allNodeIds.add(eid);
        }
        // 添加边：parent -> child（基于 GroupResult.newParentIds）
        if (operatRel != null) {
            for (GroupResult gr : operatRel) {
                List<NodeInfo> nodes = gr.getNodeInfoList();
                if (nodes == null) continue;
                for (NodeInfo node : nodes) {
                    Integer nodeId = node.getGlobalSerial();
                    dag.putIfAbsent(nodeId, new ArrayList<>());
                    allNodeIds.add(nodeId);
                    List<Integer> childIds = node.getNewChildIds();
                    if (childIds != null) {



                        for (Integer childId : childIds) {

                                dag.get(nodeId).add(childId);
                                allNodeIds.add(childId);

                        }
                    }
                }
            }
        }

        // ---- 3. 计算入度 ----
        Map<Integer, Integer> inDegree = new HashMap<>();
        for (Integer nodeId : allNodeIds) {
            inDegree.put(nodeId, 0);
        }
        for (Integer parent : dag.keySet()) {
            for (Integer child : dag.get(parent)) {
                inDegree.merge(child, 1, Integer::sum);
            }
        }

        // ---- 4. 拓扑排序 + 动态规划找最长路径 ----
        // earliest[nodeId] = 从任意根节点到 nodeId 的最长路径
        Map<Integer, Double> earliest = new HashMap<>();
        Queue<Integer> queue = new LinkedList<>();

        // 初始化：入度为 0 的节点 earliest = processingTime
        for (Integer nodeId : allNodeIds) {
            if (inDegree.get(nodeId) == 0) {
                double pt = entryProcessingTime.getOrDefault(nodeId, 0.0);
                earliest.put(nodeId, pt);
                queue.offer(nodeId);
            }
        }

        while (!queue.isEmpty()) {
            Integer parent = queue.poll();
            double parentEarliest = earliest.getOrDefault(parent, 0.0);
            double parentPt = entryProcessingTime.getOrDefault(parent, 0.0);
            double parentFinish = parentEarliest; // finish = start + pt，这里 start = parentEarliest - pt

            for (Integer child : dag.getOrDefault(parent, Collections.emptyList())) {
                double childPt = entryProcessingTime.getOrDefault(child, 0.0);
                double childEarliestCandidate = parentEarliest + childPt;
                earliest.put(child, Math.max(earliest.getOrDefault(child, 0.0), childEarliestCandidate));
                inDegree.merge(child, -1, Integer::sum);
                if (inDegree.get(child) == 0) {
                    queue.offer(child);
                }
            }
        }

        // ---- 5. 关键路径 = 所有节点 earliest 的最大值 ----
        double criticalPath = 0.0;
        for (double val : earliest.values()) {
            if (val > criticalPath) criticalPath = val;
        }
        return criticalPath;
    }

    /**
     * 计算 FlowTime（总流程时间）的理论下界 = 所有工序的加工时间之和。
     */
    private static double computeFlowTimeLowerBound(Chromosome chromosome) {
        List<GAScheduleResult> result = chromosome.getResult();
        if (result == null || result.isEmpty()) return 0.0;
        return result.stream()
                .mapToDouble(r -> Math.max(1.0, r.getProcessingTime()))
                .sum();
    }

    /**
     * 计算总 Tardiness 的 Gap。
     * lowerBound = 0，所以 Gap = current（即延迟小时数的绝对值）。
     * 结果为 (current - 0) / 1 = current，与延迟同量纲。
     */
    public static double computeTardinessGap(double tardiness) {
        if (tardiness <= 0) return 0.0;
        return tardiness; // lowerBound=0，用 1 做分母
    }

    /**
     * 计算 Makespan Gap。
     */
    public static double computeMakespanGap(double makespan, double lowerBound) {
        if (lowerBound <= 0) return 0.0;
        return (makespan - lowerBound) / lowerBound;
    }

    /**
     * 计算 FlowTime Gap。
     */
    public static double computeFlowTimeGap(double flowTime, double lowerBound) {
        if (lowerBound <= 0) return 0.0;
        return (flowTime - lowerBound) / lowerBound;
    }

    /**
     * 计算 MachineLoad Gap（标准差的下界为 0）。
     */
    public static double computeMachineLoadGap(double machineLoadStd) {
        if (machineLoadStd <= 0) return 0.0;
        return machineLoadStd; // lowerBound=0
    }

    /**
     * 格式化 Gap 为百分比字符串。
     */
    public static String formatGap(double gap) {
        if (gap == 0.0) return "0.00%";
        return String.format("%.2f%%", gap * 100.0);
    }

    /**
     * 生成 KPI Gap 报告字符串（供调用方直接打印）。
     * 格式：`KPI Gap 报告 (越小越好): [Makespan: cur / LB → Gap=x%] [FlowTime: ...] ...`
     */
    public static String generateGapReport(Chromosome chromosome) {
        double[] objectives = chromosome.getObjectives();
        double[] lowerBounds = chromosome.getLowerBoundObjectives();
        if (objectives == null || objectives.length == 0) {
            return "KPI Gap 报告: 无 objectives 数据";
        }
        String[] names = {"Makespan", "FlowTime", "SetupTime", "MachineLoad", "Tardiness"};
        StringBuilder sb = new StringBuilder("KPI Gap 报告 (越小越好): ");
        for (int i = 0; i < objectives.length; i++) {
            double current = objectives[i];
            double lb = (lowerBounds != null && i < lowerBounds.length) ? lowerBounds[i] : 0.0;
            double gap;
            if (lb > 0) {
                gap = (current - lb) / lb;
            } else if (current > 0) {
                gap = current; // lowerBound=0 时 gap = current 本身（绝对偏离量）
            } else {
                gap = 0.0;
            }
            String name = i < names.length ? names[i] : ("Obj[" + i + "]");
            sb.append(String.format("[%s: %.4f / LB=%.4f → Gap=%s] ",
                    name, current, lb, formatGap(gap)));
        }
        return sb.toString();
    }
}
