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.Algorithm.IDAndChildID.GroupResult;
import com.aps.entity.basic.*;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 禁忌搜索算法：负责禁忌表的生命周期与搜索主循环。
 *
 * - VNS / SA 通过本类的公共方法 {@link #isTabu(String)}、{@link #addToTabuList(String)}
 *   来访问禁忌表，避免各自维护一套禁忌逻辑。
 * - 同时也可以作为独立阶段（由 HybridAlgorithm 调用 {@link #search}) 执行完整的禁忌搜索优化。
 *
 * 核心思想：
 *   1) 三粒度禁忌 key：machineStr(机器分配整体) / operationStr(工序排序整体) / geneStr(完整编码)
 *   2) 渴望准则：若邻居优于 best，则无论是否命中禁忌都接受
 *   3) 劣解概率接受：0.45 -> 0.05，随迭代递减
 *   4) 时间预算：避免在大规模问题上跑太久
 *
 * 作者：佟礼
 */
public class TabuSearch {

    // ==================== 改进判断参数（public，供 VNS / SA 共享） ====================
    public static final double SIGNIFICANT_IMPROVEMENT_THRESHOLD = 1e-11;   // 显著改进阈值
    public static final double MINOR_IMPROVEMENT_THRESHOLD = 0.0;             // 微小改进阈值（any positive improvement）

    // ==================== 劣解接受概率（public，供 VNS 共享） ====================
    public static final double WORSE_ACCEPT_PROB_START = 0.45;
    public static final double WORSE_ACCEPT_PROB_MIN = 0.05;

    // ==================== 时间预算（public，供 VNS 共享） ====================
    public static final long TS_TIME_BUDGET_MS = 25L * 60L * 1000L;
    public static final long TS_PER_ITER_BUDGET_MS = 17L * 1000L;

    // ==================== 依赖项 ====================
    private final Map<Long, Integer> tsBottleneckMachineFrequency = new HashMap<>();
    private final Random tsRnd = new Random(20260622L);

    private final List<Entry> cachedAllOperations;
    private final FitnessCalculator fitnessCalculator;

    // ==================== 禁忌表：List + HashSet 组合 ====================
    private final List<String> tabuList = new ArrayList<>();
    private final Set<String> tabuSet = new HashSet<>();
    private final int tabuListSize;

    public TabuSearch(List<Entry> allOperations, List<Order> orders,
                      TreeMap<String, Material> materials, List<GroupResult> entryRel,
                      FitnessCalculator fitnessCalculator) {
        this.fitnessCalculator = fitnessCalculator;
        this.cachedAllOperations = ProductionDeepCopyUtil.deepCopyList(
                new CopyOnWriteArrayList<>(allOperations), Entry.class);
        // 禁忌长度随问题规模自适应，避免太小/太大
        this.tabuListSize = Math.min(150, Math.max(50, allOperations.size() / 30));
    }

    // ====================================================================
    // 公共方法：供 VNS / SA 调用，用于共享禁忌表
    // ====================================================================

    /**
     * 判断 key 是否在禁忌表中。
     */
    public boolean isTabu(String key) {
        if (key == null) return false;
        return tabuSet.contains(key);
    }

    /**
     * 添加 key 到禁忌表（FIFO 策略）。
     */
    public void addToTabuList(String key) {
        if (key == null) return;
        if (tabuSet.contains(key)) return;
        tabuList.add(key);
        tabuSet.add(key);
        while (tabuList.size() > tabuListSize) {
            String removed = tabuList.remove(0);
            tabuSet.remove(removed);
        }
    }

    /**
     * 批量把 Chromosome 的三层粒度 key 加入禁忌表。
     */
    public void addChromosomeToTabu(Chromosome c) {
        if (c == null) return;
        if (c.getMachineStr() != null) addToTabuList(c.getMachineStr());
        if (c.getOperationStr() != null) addToTabuList(c.getOperationStr());
        if (c.getGeneStr() != null) addToTabuList(c.getGeneStr());
    }

    /**
     * 判断 Chromosome 是否命中任意一层禁忌。
     */
    public boolean isChromosomeTabu(Chromosome c) {
        if (c == null) return false;
        if (c.getMachineStr() != null && isTabu(c.getMachineStr())) return true;
        if (c.getOperationStr() != null && isTabu(c.getOperationStr())) return true;
        if (c.getGeneStr() != null && isTabu(c.getGeneStr())) return true;
        return false;
    }

    // ====================================================================
    // 搜索主循环（独立阶段）
    // ====================================================================

    /**
     * 独立的禁忌搜索阶段（从 HybridAlgorithm 调用）。
     *
     * @param chromosome 初始解
     * @param vns        提供邻域生成、瓶颈感知策略
     * @param decoder    解码与调度
     * @param machines   机器列表
     */
    public Chromosome search(Chromosome chromosome, VariableNeighborhoodSearch vns,
                             GeneticDecoder decoder, List<Machine> machines) {
        FileHelper.writeLogFile("禁忌搜索(融合版) - 开始执行");

        Chromosome current = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
        Chromosome best = ProductionDeepCopyUtil.deepCopy(chromosome, Chromosome.class);
        double currentBestFitness = best.getFitness();

        int iterations = 0;
        int improveCount = 0;
        int noImprovementCount = 0;
        int maxNoImprovement = 15;

        int stagnantWindow = 15;
        int[] recentImprovements = new int[stagnantWindow];

        // 时间预算驱动的 maxIterations
        long tsStartTimeMs = System.currentTimeMillis();
        long remainingBudgetMs = Math.max(5L * 60L * 1000L, TS_TIME_BUDGET_MS / 2);
        int timeBasedMaxIter = (int) Math.max(30, remainingBudgetMs / TS_PER_ITER_BUDGET_MS);
        int sizeBasedMaxIter = Math.max(60, cachedAllOperations.size() / 50);
        int maxIterations = Math.min(Math.max(60, sizeBasedMaxIter), Math.max(150, timeBasedMaxIter));

        FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 参数: 最大迭代=%d, 禁忌长度=%d, 时间预算=%.1fmin",
                maxIterations, tabuListSize, (double) remainingBudgetMs / 60000.0));

        for (int i = 0; i < maxIterations; i++) {
            iterations++;
            decoder.DelOrder(current);

            // ---- 1) 生成邻域 ----
            Chromosome neighbor = vns.generateNeighbor(current);
            if (neighbor == null) {
                noImprovementCount++;
                if (iterations <= stagnantWindow) recentImprovements[iterations - 1] = 0;
                continue;
            }

            // ---- 2) 禁忌检查 ----
            boolean tabuHit = isChromosomeTabu(neighbor);
            boolean sameAsCurrent = (neighbor.getGeneStr() != null
                    && neighbor.getGeneStr().equals(current.getGeneStr()));
            if (sameAsCurrent) {
                addChromosomeToTabu(neighbor);
                noImprovementCount++;
                if (iterations <= stagnantWindow) recentImprovements[iterations - 1] = 0;
                continue;
            }

            // ---- 3) 解码 ----
            decode(decoder, neighbor, machines);
            addChromosomeToTabu(neighbor);

            // ---- 4) 接受逻辑 ----
            boolean betterThanBest = isBetter(neighbor, best);
            boolean betterThanCurrent = isBetter(neighbor, current);

            boolean accept;
            if (betterThanBest) {
                // 渴望准则：优于 best 无条件接受
                accept = true;
            } else if (!tabuHit && betterThanCurrent) {
                accept = true;
            } else if (!tabuHit) {
                // 非禁忌但劣解：按递减概率接受
                double progress = Math.min(1.0, (double) iterations / (double) Math.max(30, maxIterations));
                double acceptProb = WORSE_ACCEPT_PROB_START -
                        (WORSE_ACCEPT_PROB_START - WORSE_ACCEPT_PROB_MIN) * progress;
                accept = tsRnd.nextDouble() < acceptProb;
            } else {
                accept = false;
            }

            if (accept) {
                current = ProductionDeepCopyUtil.deepCopy(neighbor, Chromosome.class);
                if (betterThanBest) {
                    best = ProductionDeepCopyUtil.deepCopy(neighbor, Chromosome.class);
                    improveCount++;
                    double delta = best.getFitness() - currentBestFitness;
                    if (delta > SIGNIFICANT_IMPROVEMENT_THRESHOLD) {
                        noImprovementCount = 0;
                        currentBestFitness = best.getFitness();
                        FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 找到更好解(显著), 迭代=%d, fitness=%.12f",
                                iterations, best.getFitness()));
                    } else {
                        // 只要是严格正向改进，都清零计数，避免过早退出
                        if (delta > MINOR_IMPROVEMENT_THRESHOLD) {
                            noImprovementCount = 0;
                            currentBestFitness = best.getFitness();
                        }
                        FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 找到更好解(微小), 迭代=%d, fitness=%.12f, delta=%.2e",
                                iterations, best.getFitness(), delta));
                    }
                }
            } else {
                noImprovementCount++;
                if (iterations <= stagnantWindow) recentImprovements[iterations - 1] = 0;
            }

            // ---- 5) 提前停止检查 ----
            if (noImprovementCount >= maxNoImprovement) {
                FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 提前停止: 连续%d次无改进", maxNoImprovement));
                break;
            }
            long elapsedMs = System.currentTimeMillis() - tsStartTimeMs;
            if (elapsedMs > remainingBudgetMs) {
                FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 提前停止: 达到时间预算(%.1fmin)",
                        elapsedMs / 60000.0));
                break;
            }
        }

        FileHelper.writeLogFile(String.format("禁忌搜索(融合版) - 结束: 总迭代=%d, 改进次数=%d, 最终fitness=%.12f",
                iterations, improveCount, best.getFitness()));
        return best;
    }

    // ====================================================================
    // 辅助方法
    // ====================================================================

    private void decode(GeneticDecoder decoder, Chromosome chromosome, List<Machine> machines) {
        chromosome.setResult(new CopyOnWriteArrayList<>());
        chromosome.setMachines(ProductionDeepCopyUtil.deepCopyList(machines, Machine.class));
        chromosome.setOrders(ProductionDeepCopyUtil.deepCopyList(
                new CopyOnWriteArrayList<>(chromosome.getOrders()), Order.class));
        chromosome.setOperatRel(ProductionDeepCopyUtil.deepCopyList(
                new CopyOnWriteArrayList<>(chromosome.getOperatRel()), GroupResult.class));
        chromosome.setMaterials(ProductionDeepCopyUtil.deepCopyTreeMap(
                chromosome.getMaterials(), String.class, Material.class));
        chromosome.setAllOperations(ProductionDeepCopyUtil.deepCopyList(
                new CopyOnWriteArrayList<>(cachedAllOperations), Entry.class));

        List<GAScheduleResult> lockedOrders = getLockedOrders(chromosome);
        if (lockedOrders != null && !lockedOrders.isEmpty()) {
            chromosome.setResultOld(ProductionDeepCopyUtil.deepCopyList(lockedOrders, GAScheduleResult.class));
        } else {
            chromosome.setResultOld(new CopyOnWriteArrayList<>());
        }
        decoder.decodeChromosomeWithCache(chromosome, false);
    }

    @SuppressWarnings("unchecked")
    private List<GAScheduleResult> getLockedOrders(Chromosome chromosome) {
        try {
            return (List<GAScheduleResult>) com.aps.common.util.GlobalCacheUtil.get("locked_orders_" + chromosome.getScenarioID());
        } catch (Exception ignored) {
            return null;
        }
    }

    private boolean isBetter(Chromosome c1, Chromosome c2) {
        return fitnessCalculator.isBetter(c1, c2);
    }

    /**
     * 估计两个逗号分隔字符串的汉明距离（位置不同的元素数）。
     * 辅助判断"是否只是微调"，供调用方使用。
     */
    public int estimateHammingDistance(String a, String b) {
        if (a == null || b == null) return Integer.MAX_VALUE;
        if (a.isEmpty() || b.isEmpty()) return Integer.MAX_VALUE;
        String[] sa = a.split(",");
        String[] sb = b.split(",");
        int minLen = Math.min(sa.length, sb.length);
        int diff = 0;
        for (int i = 0; i < minLen; i++) {
            if (!sa[i].equals(sb[i])) diff++;
        }
        diff += Math.abs(sa.length - sb.length);
        return diff;
    }
}
