package com.aps.macroplanner.objective;

import com.google.ortools.linearsolver.MPVariable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 策略层级配置 — 对应 Quintiq 中的 StrategyLevel
 *
 * <p>每个层级包含一组 KPI，在分层优化中作为一个整体求解。
 * 层级越低（level 值越小），优先级越高，越先求解。</p>
 *
 * <h3>分层优化流程</h3>
 * <pre>
 *   第1轮: 只优化 Level 1 的 KPI → 记录最优值 V1
 *   第2轮: 约束 Level1 ≤ V1×(1+slack) → 优化 Level 2 的 KPI
 *   第3轮: 约束 Level1+2 ≤ 最优×(1+slack) → 优化 Level 3 的 KPI
 *   ...
 * </pre>
 *
 * <h3>默认层级划分</h3>
 * <pre>
 *   Level 1 (需求满足):   Fulfillment (需求缺口 = 硬约束)
 *   Level 2 (产能):       UnitCapacity (产能超载 = 物理约束)
 *   Level 3 (业务KPI):    LotSize, TargetInventory, SupplyTarget, SalesPriority
 *   Level 4 (软约束):     MaxInventory, MinInventory, MinSupply, MaxSupply
 * </pre>
 *
 * @see ObjectiveBuilder
 * @see com.aps.macroplanner.MacroPlannerOptimizer
 */
public class StrategyLevel {

    /** 层级编号 (1 最高优先, 数值越大优先级越低) */
    private final int level;

    /** 层级名称 (用于日志输出) */
    private final String name;

    /**
     * 目标松弛比例 — 允许上层最优值退化的比例。
     * 例如 0.0 表示不允许退化（严格分层），
     * 0.05 表示允许上层目标值恶化 5% 以换取下层优化空间。
     * 对应 Quintiq 中的 RelativeGoalSlack。
     */
    private final double relativeGoalSlack;

    /** 该层级包含的 KPI 条目列表 */
    private final List<KPIEntry> kpis = new ArrayList<>();

    /**
     * 单个 KPI 条目 — 封装变量、权重和方向。
     */
    public static class KPIEntry {
        /** 该 KPI 的汇总变量 (如 TotalFulfillment) */
        public final MPVariable variable;

        /** 该 KPI 在当前层级内的权重 */
        public final double weight;

        /** 是否为负向 KPI (越小越好 = 正常惩罚项; false = 正常惩罚项) */
        public final boolean isNegative;

        /** KPI 名称 (用于日志) */
        public final String name;

        public KPIEntry(String name, MPVariable variable, double weight, boolean isNegative) {
            this.name = name;
            this.variable = variable;
            this.weight = weight;
            this.isNegative = isNegative;
        }

        /**
         * 计算该 KPI 在目标函数中的实际系数。
         * 负向 KPI (如 SalesDemandPriority) 使用负系数实现最大化。
         */
        public double effectiveCoefficient() {
            return isNegative ? -weight : weight;
        }
    }

    /**
     * 创建策略层级。
     *
     * @param level             层级编号 (1-based, 越小优先级越高)
     * @param name              层级名称
     * @param relativeGoalSlack 目标松弛比例 (0.0 = 严格分层)
     */
    public StrategyLevel(int level, String name, double relativeGoalSlack) {
        this.level = level;
        this.name = name;
        this.relativeGoalSlack = relativeGoalSlack;
    }

    /**
     * 添加一个 KPI 到该层级。
     *
     * @param name     KPI 名称
     * @param variable KPI 汇总变量
     * @param weight   权重 (0 = 跳过)
     */
    public void addKPI(String name, MPVariable variable, double weight) {
        addKPI(name, variable, weight, false);
    }

    /**
     * 添加一个 KPI 到该层级。
     *
     * @param name       KPI 名称
     * @param variable   KPI 汇总变量
     * @param weight     权重 (0 = 跳过)
     * @param isNegative 是否为负向 KPI (true = 最大化, 使用负系数)
     */
    public void addKPI(String name, MPVariable variable, double weight, boolean isNegative) {
        if (weight > 0.0 && variable != null) {
            kpis.add(new KPIEntry(name, variable, weight, isNegative));
        }
    }

    // ==================== Getters ====================

    public int getLevel() { return level; }
    public String getName() { return name; }
    public double getRelativeGoalSlack() { return relativeGoalSlack; }
    public List<KPIEntry> getKpis() { return Collections.unmodifiableList(kpis); }

    /**
     * 该层级是否有任何有效的 KPI。
     */
    public boolean hasKpis() {
        return !kpis.isEmpty();
    }
}