package com.aps.service.Algorithm;

import com.aps.common.util.FileHelper;
import com.aps.common.util.GlobalCacheUtil;
import com.aps.common.util.ProductionDeepCopyUtil;
import com.aps.entity.Algorithm.Chromosome;
import com.aps.entity.Algorithm.GAScheduleResult;
import com.aps.entity.Algorithm.GlobalOperationInfo;
import com.aps.entity.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.Algorithm.ObjectiveWeights;
import com.aps.entity.basic.*;
import com.google.ortools.Loader;
import com.google.ortools.sat.CpModel;
import com.google.ortools.sat.CpSolver;
import com.google.ortools.sat.CpSolverStatus;
import com.google.ortools.sat.IntVar;
import com.google.ortools.sat.IntervalVar;
import com.google.ortools.sat.LinearArgument;
import com.google.ortools.sat.LinearExpr;
import com.google.ortools.sat.Literal;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * LNS (Large Neighborhood Search) + OR-Tools CP-SAT
 *
 * 从 NSGA-II 帕累托前沿取一个染色体：
 *   1. 随机"释放" 10%~30% 的工序（让 CP-SAT 重排）
 *   2. 其余工序"冻结"（保持机器和时间不变）
 *   3. CP-SAT 在 5~15 秒内给出一个更好的调度
 *   4. decode 后若目标改进则接受，否则回退
 *
 * 循环多轮。
 */
public class CpSatLnsNeighborhood {

    private static boolean NATIVE_LIBRARY_LOADED = false;

    static {
        try {
            Loader.loadNativeLibraries();
            Class.forName("com.google.ortools.sat.CpModel");
            NATIVE_LIBRARY_LOADED = true;
            FileHelper.writeLogFile("[CpSatLns] OR-Tools 已加载");
        } catch (Throwable t) {
            FileHelper.writeLogFile("[CpSatLns] OR-Tools 本机库不可用，LNS 被禁用：" + t.getMessage());
        }
    }

    private final List<Entry> allOperations;
    private final List<Machine> machines;
    private final FitnessCalculator fitnessCalculator;

    private  GeneticOperations geneticOperations;

    private List<Machine> cachedMachines;
    private List<Order> cachedOrders;
    private List<GroupResult> cachedEntryRel;
    private TreeMap<String, Material> cachedMaterials;
    private List<Entry> cachedAllOperations;

    private final Random random = new Random(20260618L);

    /**
     * @param allOperations   全工序列表
     * @param machines        全机器列表
     * @param fitnessCalculator 适应度计算器（与 HybridAlgorithm 一致）
     */
    public CpSatLnsNeighborhood(List<Entry> allOperations, List<Machine> machines,List<Order> orders,
                                TreeMap<String, Material> materials,List<GroupResult> entryRel,
                                FitnessCalculator fitnessCalculator) {
        this.allOperations = allOperations;
        this.machines = machines;
        this.fitnessCalculator = fitnessCalculator;
        geneticOperations=new GeneticOperations();

        // 预缓存解码需要的深拷贝列表，避免重复拷贝
        cachedAllOperations = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(allOperations), Entry.class);
        cachedOrders = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(orders), Order.class);
        cachedEntryRel = ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(entryRel), GroupResult.class);
        cachedMaterials = ProductionDeepCopyUtil.deepCopyTreeMap(materials, String.class, Material.class);

    }

    /**
     * 对帕累托前沿做 LNS 重优化。
     * 返回的 list 包含：改进后的前沿 + 原始前沿（让下游的帕累托归并去重）
     */
    public List<Chromosome> runLnsOnParetoFront(List<Chromosome> paretoFront, GeneticDecoder sharedDecoder) {
        if (!NATIVE_LIBRARY_LOADED || paretoFront == null || paretoFront.isEmpty()) return paretoFront;

        int totalOps = allOperations.size();
        int lnsOuterRounds = Math.min(paretoFront.size(), 8);
        int lnsInnerRounds = Math.max(3, Math.min(15, 8000 / Math.max(100, totalOps)));
        int releasedOps = Math.max(50, (int)(totalOps * 0.15));
        int cpsatTimeSeconds = Math.max(5, Math.min(15, 12000 / Math.max(100, totalOps)));

        if(totalOps<50)
        {
            return  null;
        }

        FileHelper.writeLogFile("[CpSatLns] 开始 LNS 重优化：工序=" + totalOps
                + "，每轮释放=" + releasedOps + "，CP-SAT=" + cpsatTimeSeconds + "s，"
                + "前沿解数=" + lnsOuterRounds + "，每解迭代=" + lnsInnerRounds);

        List<Chromosome> newFront = new ArrayList<>();
        int totalAttempts = 0, totalImproves = 0;

        for (int i = 0; i < lnsOuterRounds && i < paretoFront.size(); i++) {
            Chromosome current = lightCopy(paretoFront.get(i));
            Chromosome beat = ProductionDeepCopyUtil.deepCopy(paretoFront.get(i), Chromosome.class);

            // sharedDecoder.serialDecode(current);
            double currentFitness = current.getFitness();

            double[] curObj = current.getObjectives();
            FileHelper.log("[CpSatLns] 帕累托前沿第" + (i + 1) + "个解 → 适应度="
                    + String.format("%.4f", currentFitness)
                    + (curObj != null ? " makespan=" + String.format("%.1f", curObj[0]) : ""),true);

            for (int round = 0; round < lnsInnerRounds; round++) {
                totalAttempts++;
                Chromosome neighbor = optimizeNeighborhood(current, releasedOps, cpsatTimeSeconds);
                if (neighbor == null) break;

                decode(sharedDecoder,neighbor,machines);

                if (fitnessCalculator.isBetter(neighbor, current)) {
                    double[] nObj = neighbor.getObjectives();
                    FileHelper.log("[CpSatLns]   第" + (round + 1) + "轮 ✅接受"
                            + " 新适应度=" + String.format("%.4f", neighbor.getFitness())
                            + " 旧适应度=" + String.format("%.4f", currentFitness)
                            + (nObj != null ? " 新makespan=" + String.format("%.1f", nObj[0]) : ""),true);
                    current = neighbor;
                    beat = ProductionDeepCopyUtil.deepCopy(neighbor, Chromosome.class);

                    currentFitness = neighbor.getFitness();
                    totalImproves++;
                }
            }
            newFront.add(beat);
        }

        newFront.addAll(paretoFront);

        FileHelper.log("[CpSatLns] 结束：尝试=" + totalAttempts
                + "，改进=" + totalImproves + "，合并后解数=" + newFront.size(),true);
        return newFront;
    }

    // ----------------------------------------------------------------
    // 单轮邻域重优化
    // ----------------------------------------------------------------
    public Chromosome optimizeNeighborhood(Chromosome base, int releaseCount, int timeLimitSec) {

        List<GlobalOperationInfo> globalOpList = base.getGlobalOpList();
        if (globalOpList == null || globalOpList.isEmpty()) return null;

        CopyOnWriteArrayList<Integer> ms = base.getMachineSelection();
        if (ms == null) return null;

        int totalCount = globalOpList.size();
        releaseCount = Math.min(releaseCount, totalCount);

        long horizon = 30L * 24 * 60 * 60; // 30 天（秒）

        // ================== 1. 从 Result 中读取每道工序的机器+时间 ==================
        Map<Integer, GAScheduleResult> scheduleMap = new HashMap<>();
        if (base.getResult() != null) {
            for (GAScheduleResult r : base.getResult()) {
                int key = (r.getGroupId() * 1000000) + r.getSeq();
                scheduleMap.put(key, r);
            }
        }

        long[] opStartSec = new long[totalCount];
        long[] opDurationSec = new long[totalCount];
        long[] opMachineId = new long[totalCount];

        for (int i = 0; i < totalCount; i++) {
            GlobalOperationInfo info = globalOpList.get(i);
            Entry op = info.getOp();
            int key = (info.getGroupId() * 1000000) + info.getSequence();
            GAScheduleResult res = scheduleMap.get(key);

            if (res != null) {
                opStartSec[i] = res.getStartTime();
                opMachineId[i] = res.getMachineId();
                opDurationSec[i] = Math.max(60L, (long)(res.getProcessingTime() * 60));
            } else {
                opDurationSec[i] = Math.max(60L, (long)(op.getMinProcessingTime() * 60));
                opStartSec[i] = 0;
                List<MachineOption> options = op.getMachineOptions();
                opMachineId[i] = (options != null && !options.isEmpty()) ? options.get(0).getMachineId() : -1;
            }
        }

        // ================== 2. 随机选"释放"的工序 ==================
        BitSet released = new BitSet(totalCount);
        if (releaseCount >= totalCount) {
            released.set(0, totalCount);
        } else {
            List<Integer> indices = IntStream.range(0, totalCount)
                    .boxed().collect(Collectors.toList());
            Collections.shuffle(indices, random);
            for (int k = 0; k < releaseCount; k++) released.set(indices.get(k));
        }

        // ================== 3. 构建 CP-SAT 模型 ==================
        try {
            CpModel model = new CpModel();

            Map<Long, Integer> machineIdx = new HashMap<>();
            for (int m = 0; m < machines.size(); m++) machineIdx.put(machines.get(m).getId(), m);

            List<List<IntervalVar>> machineIntervals = new ArrayList<>();
            for (int m = 0; m < machines.size(); m++) machineIntervals.add(new ArrayList<>());

            IntVar[] startVars = new IntVar[totalCount];
            // presence[i][j] = 第 i 个释放工序选第 j 台机器的布尔
            List<Literal[]> presenceMatrix = new ArrayList<>();

            // 处理释放工序
            for (int i = 0; i < totalCount; i++) {
                List<MachineOption> options = globalOpList.get(i).getOp().getMachineOptions();
                presenceMatrix.add(null);
                if (!released.get(i)) continue;
                if (options == null || options.isEmpty()) continue;

                long baseStart = Math.max(0, opStartSec[i]);
                long lower = Math.max(0, baseStart - 3600);
                long upper = Math.min(horizon, baseStart + 10800);
                if (upper <= lower) upper = lower + 3600;

                startVars[i] = model.newIntVar(lower, upper, "start_" + i);

                List<Literal> presList = new ArrayList<>();
                for (int j = 0; j < options.size(); j++) {
                    MachineOption mo = options.get(j);
                    long procSec = Math.max(60L, (long)(mo.getProcessingTime() * 60));
                    Literal pres = model.newBoolVar("pres_" + i + "_" + j);
                    presList.add(pres);

                    IntVar endVar = model.newIntVar(lower + procSec, upper + procSec,
                            "end_" + i + "_" + j);
                    model.addEquality(LinearExpr.sum(new LinearArgument[]{
                            startVars[i], LinearExpr.constant(procSec)}), endVar);

                    IntervalVar iv = model.newOptionalIntervalVar(
                            startVars[i], model.newConstant(procSec), endVar, pres,
                            "iv_" + i + "_m" + mo.getMachineId());
                    int mIdx = machineIdx.getOrDefault(mo.getMachineId(), -1);
                    if (mIdx >= 0) machineIntervals.get(mIdx).add(iv);
                }

                Literal[] arr = presList.toArray(new Literal[0]);
                if (arr.length > 0) {
                    model.addExactlyOne(arr);
                    presenceMatrix.set(i, arr);
                }
            }

            // 冻结工序：固定 interval
            for (int i = 0; i < totalCount; i++) {
                if (released.get(i)) continue;
                long start = Math.max(0, opStartSec[i]);
                long dur = Math.max(60L, opDurationSec[i]);
                long mId = opMachineId[i];
                int mIdx = machineIdx.getOrDefault(mId, -1);
                if (mIdx >= 0) {
                    IntervalVar iv = model.newFixedInterval(start, dur, "frozen_" + i);
                    machineIntervals.get(mIdx).add(iv);
                }
            }

            // ================== 4. 同订单工序顺序 ==================
            Map<Integer, List<Integer>> orderOpsMap = new LinkedHashMap<>();
            for (int i = 0; i < totalCount; i++) {
                orderOpsMap.computeIfAbsent(
                        globalOpList.get(i).getGroupId(), k -> new ArrayList<>()).add(i);
            }
            for (List<Integer> ops : orderOpsMap.values()) {
                ops.sort(Comparator.comparingInt(idx -> globalOpList.get(idx).getSequence()));
                for (int k = 0; k < ops.size() - 1; k++) {
                    int prev = ops.get(k);
                    int next = ops.get(k + 1);
                    long prevDuration = Math.max(60L, opDurationSec[prev]);

                    LinearArgument prevEnd;
                    LinearArgument nextStart;
                    if (released.get(prev)) {
                        prevEnd = LinearExpr.sum(new LinearArgument[]{
                                startVars[prev], LinearExpr.constant(prevDuration)});
                    } else {
                        prevEnd = LinearExpr.constant(opStartSec[prev] + prevDuration);
                    }
                    if (released.get(next)) {
                        nextStart = startVars[next];
                    } else {
                        nextStart = LinearExpr.constant(opStartSec[next]);
                    }
                    model.addLessOrEqual(prevEnd, nextStart);
                }
            }

            // ================== 5. 每台机器 no_overlap ==================
            for (List<IntervalVar> list : machineIntervals) {
                if (list.size() > 1) {
                    model.addNoOverlap(list.toArray(new IntervalVar[0]));
                }
            }

            // ================== 6. 目标：makespan + 优先级加权 + 延迟 + 负载均衡 ==================
            IntVar makespan = model.newIntVar(0, horizon, "makespan");
            List<LinearArgument> objTerms = new ArrayList<>();
            List<Long> objWeights = new ArrayList<>();

            objTerms.add(makespan);
            objWeights.add(100L);

            double maxPriority = 1.0;
            for (int i = 0; i < totalCount; i++) {
                maxPriority = Math.max(maxPriority, globalOpList.get(i).getOp().getPriority());
            }

            for (int i = 0; i < totalCount; i++) {
                long dur = Math.max(60L, opDurationSec[i]);
                LinearArgument endVar;
                if (released.get(i)) {
                    endVar = LinearExpr.sum(new LinearArgument[]{
                            startVars[i], LinearExpr.constant(dur)});
                } else {
                    endVar = LinearExpr.constant(opStartSec[i] + dur);
                }
                model.addLessOrEqual(endVar, makespan);

                if (released.get(i)) {
                    double priority = globalOpList.get(i).getOp().getPriority();
                    long weight = (long)(maxPriority - priority + 1);
                    objTerms.add(endVar);
                    objWeights.add(weight);
                }
            }

            // 延迟时间（只对释放的工序所属订单）
            long tardinessWeight = 80L;
            Map<Integer, List<LinearArgument>> groupEnds = new HashMap<>();
            for (int i = 0; i < totalCount; i++) {
                if (!released.get(i)) continue;
                GlobalOperationInfo info = globalOpList.get(i);
                groupEnds.computeIfAbsent(info.getGroupId(), k -> new ArrayList<>())
                        .add(LinearExpr.sum(new LinearArgument[]{
                                startVars[i], LinearExpr.constant(Math.max(60L, opDurationSec[i]))}));
            }
            for (Map.Entry<Integer, List<LinearArgument>> entry : groupEnds.entrySet()) {
                IntVar lastEnd = model.newIntVar(0, horizon, "lastEnd_g" + entry.getKey());
                model.addMaxEquality(lastEnd, entry.getValue().toArray(new LinearArgument[0]));
                // 简化：延迟作为惩罚项加入（实际可加 due_date 判断）
                objTerms.add(lastEnd);
                objWeights.add(tardinessWeight);
            }

            // 机器负载均衡
            long loadWeight = 30L;
            Map<Long, List<LinearArgument>> machineEnds = new HashMap<>();
            for (int i = 0; i < totalCount; i++) {
                if (!released.get(i)) continue;
                GlobalOperationInfo info = globalOpList.get(i);
                List<com.aps.entity.basic.MachineOption> options = info.getOp().getMachineOptions();
                if (options == null || options.isEmpty()) continue;
                LinearArgument endVar = LinearExpr.sum(new LinearArgument[]{
                        startVars[i], LinearExpr.constant(Math.max(60L, opDurationSec[i]))});
                for (com.aps.entity.basic.MachineOption mo : options) {
                    machineEnds.computeIfAbsent(mo.getMachineId(), k -> new ArrayList<>()).add(endVar);
                }
            }
            for (Map.Entry<Long, List<LinearArgument>> entry : machineEnds.entrySet()) {
                IntVar machineMk = model.newIntVar(0, horizon, "mk_m" + entry.getKey());
                model.addMaxEquality(machineMk, entry.getValue().toArray(new LinearArgument[0]));
                objTerms.add(machineMk);
                objWeights.add(loadWeight);
            }

            model.minimize(LinearExpr.weightedSum(
                    objTerms.toArray(new LinearArgument[0]),
                    objWeights.stream().mapToLong(Long::longValue).toArray()));

            // ================== 7. 求解 ==================
            CpSolver solver = new CpSolver();
            solver.getParameters().setMaxTimeInSeconds(timeLimitSec);
            solver.getParameters().setNumSearchWorkers(4);
            solver.getParameters().setLogSearchProgress(false);

            CpSolverStatus status = solver.solve(model);
            if (status != CpSolverStatus.OPTIMAL && status != CpSolverStatus.FEASIBLE) {
                FileHelper.writeLogFile("[CpSatLns] 邻域求解 状态=" + status + "（无解），释放=" + releaseCount + "道工序");
                return null;
            }
            long cpObjective = (long) solver.objectiveValue();
            long cpMakespan = (long) solver.value(makespan);
            FileHelper.writeLogFile("[CpSatLns] 邻域求解 状态=" + status
                    + " 释放工序=" + releaseCount
                    + " CP-SAT目标=" + String.format("%,d", cpObjective)
                    + " makespan=" + String.format("%,d秒(%.1f小时)", cpMakespan, cpMakespan / 3600.0));

            // ================== 8. 写回新染色体的 machineSelection ==================
            Chromosome neighbor = base.deepCopy();
            CopyOnWriteArrayList<Integer> newMs = new CopyOnWriteArrayList<>(
                    neighbor.getMachineSelection() != null
                            ? neighbor.getMachineSelection()
                            : new ArrayList<>()
            );
            while (newMs.size() < totalCount) newMs.add(1);

            for (int i = 0; i < totalCount; i++) {
                if (!released.get(i)) continue;
                Literal[] arr = presenceMatrix.get(i);
                if (arr == null) continue;
                for (int j = 0; j < arr.length; j++) {
                    if (solver.value(arr[j]) == 1L) {
                        newMs.set(i, j + 1);  // 1-based
                        break;
                    }
                }
            }

            neighbor.setMachineSelection(newMs);
            neighbor.setGenerateType("LNS-CPSAT");
            neighbor.setGsOrls(5);
            return neighbor;

        } catch (Exception e) {
            FileHelper.writeLogFile("[CpSatLns] 邻域重优化异常：" + e.getMessage());
            return null;
        }
    }
    /**
     * 轻量拷贝：只复制 generateNeighbor/DelOrder 需要的字段，避免全量 JSON 深拷贝导致 OOM。
     * result/machines/operatRel 等重型数据共享引用（generateNeighbor 只读，不修改）。
     */
    private Chromosome lightCopy(Chromosome source) {
        Chromosome copy = new Chromosome();
        copy.setOperationSequencing(new CopyOnWriteArrayList<>(source.getOperationSequencing()));
        copy.setMachineSelection(new CopyOnWriteArrayList<>(source.getMachineSelection()));
        copy.setGlobalOpList(new CopyOnWriteArrayList<>(source.getGlobalOpList()));
        copy.setOrders(new CopyOnWriteArrayList<>(source.getOrders()));
        copy.setAllOperations(new CopyOnWriteArrayList<>(source.getAllOperations()));
        copy.setResult(source.getResult());
        copy.setMachines(source.getMachines());
        copy.setOperatRel(new CopyOnWriteArrayList<>(source.getOperatRel()));
        copy.setScenarioID(source.getScenarioID());
        copy.setBaseTime(source.getBaseTime());
        copy.setGenerateType(source.getGenerateType());
        copy.setFitnessLevel(source.getFitnessLevel());
        copy.setFitness(source.getFitness());
        copy.setObjectives(source.getObjectives());
        geneticOperations.DelOrder(copy);
        return copy;
    }
    /**
     * 解码染色体
     */
    private void decode(GeneticDecoder decoder, Chromosome chromosome , List<Machine> machines) {
        // MS 校验：解码前检查 machineSelection 与 machineOptions 是否匹配
        List<GlobalOperationInfo> gops = chromosome.getGlobalOpList();
        List<Integer> msCheck = chromosome.getMachineSelection();
        if (gops != null && msCheck != null) {
            int msErrors = 0;
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < Math.min(gops.size(), msCheck.size()); i++) {
                Entry op = gops.get(i).getOp();
                int msVal = msCheck.get(i);
                if (op != null && op.getMachineOptions() != null
                        && (msVal < 1 || msVal > op.getMachineOptions().size())) {
                    msErrors++;
                    if (msErrors <= 3) {
                        sb.append(String.format(" [idx=%d 订单%d工序%d ms=%d range=1-%d]",
                                i, op.getGroupId(), op.getSequence(), msVal, op.getMachineOptions().size()));
                    }
                }
            }
            if (msErrors > 0) {
                FileHelper.log(String.format("decode-MS校验失败: 共%d处越界 %s", msErrors, sb.toString()));
            }
        }

        chromosome.setResult(new CopyOnWriteArrayList<>());



        // 使用缓存的列表，避免重复深拷贝
        chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines, Machine.class));
        chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(cachedOrders), Order.class));
        chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(cachedEntryRel), GroupResult.class));
        chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(cachedMaterials, String.class, Material.class));
        chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(new CopyOnWriteArrayList<>(allOperations), Entry.class));

        // 加载锁定工单到ResultOld
        List<GAScheduleResult> lockedOrders = GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
        if (lockedOrders != null && !lockedOrders.isEmpty()) {
            chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
        } else {
            chromosome.setResultOld(new CopyOnWriteArrayList<>());
        }
        decoder.decodeChromosomeWithCache(chromosome,false);
    }

}
