package com.aps.macroplanner.constraint;

import com.google.ortools.linearsolver.MPConstraint;
import com.google.ortools.linearsolver.MPSolver;
import com.google.ortools.linearsolver.MPVariable;
import com.aps.macroplanner.data.*;
import com.aps.macroplanner.model.MacroPlannerModel;

import java.util.Map;

/**
 * KPI 汇总变量及定义约束构建器
 *
 * <p>创建全局 KPI 汇总变量, 每个 KPI = 对应松弛/惩罚变量的总和。
 * 这些汇总变量被目标函数直接引用。</p>
 *
 * <h3>KPI 汇总一览</h3>
 * <pre>
 * TotalFulfillment        = Σ DemandSlack                (权重 100)
 * TotalLotSize            = Σ PTLotSizeOver + Under      (权重 10)
 * TotalMaxInventoryLevel  = Σ MaxInvQtyOver              (权重 5)
 * TotalMinInventoryLevel  = Σ MinInvQtyUnder             (权重 5)
 * TotalTargetInvLevel     = Σ InvQtyUnderTarget          (权重 8)
 * TotalUnitCapacity       = Σ CapacityOverloaded          (权重 3)
 * TotalSupplyTarget       = Σ SupplyTargetQtyUnder       (权重 8)
 * TotalMinSupply          = Σ MinSupplyQtyUnder          (权重 5)
 * TotalMaxSupply          = Σ MaxSupplyQtyOver           (权重 5)
 * TotalSalesDemandPriority= Σ SalesDemandQty × Priority  (权重 1)
 * TotalPostponementPenalty= 0 (简化版)                   (权重 20)
 * TotalProcessMaxQuantity = 0 (简化版)                   (权重 5)
 * </pre>
 */
public class KpiAggregator {

    /**
     * 创建 KPI 汇总变量及其定义约束, 并注册到模型中。
     */
    public static void build(MacroPlannerModel model, TestDataBuilder data) {
        double inf = MPSolver.infinity();

        // TotalFulfillment = Σ DemandSlack
        model.setTotalFulfillment(createSumKpi(model, "TotalFulfillment",
                model.getDemandSlackVars()));

        // TotalLotSize = Σ PTLotSizeOver + Σ PTLotSizeUnder
        MPVariable totalLotSize = model.getSolver().makeNumVar(0.0, inf, "TotalLotSize");
        MPConstraint lotDef = model.getSolver().makeConstraint(0.0, 0.0, "Def_TotalLotSize");
        lotDef.setCoefficient(totalLotSize, -1.0);
        for (MPVariable v : model.getPtLotSizeOverVars().values()) lotDef.setCoefficient(v, 1.0);
        for (MPVariable v : model.getPtLotSizeUnderVars().values()) lotDef.setCoefficient(v, 1.0);
        model.setTotalLotSize(totalLotSize);

        // TotalMaxInventoryLevel = Σ MaxInvQtyOver
        model.setTotalMaxInventoryLevel(createSumKpi(model, "TotalMaxInvLevel",
                model.getMaxInvQtyOverVars()));

        // TotalMinInventoryLevel = Σ MinInvQtyUnder
        model.setTotalMinInventoryLevel(createSumKpi(model, "TotalMinInvLevel",
                model.getMinInvQtyUnderVars()));

        // TotalTargetInvLevel = Σ InvQtyUnderTarget
        model.setTotalTargetInvLevel(createSumKpi(model, "TotalTargetInvLevel",
                model.getInvQtyUnderTargetVars()));

        // TotalUnitCapacity = Σ CapacityOverloaded
        model.setTotalUnitCapacity(createSumKpi(model, "TotalUnitCapacity",
                model.getCapacityOverloadedVars()));

        // TotalSupplyTarget = Σ SupplyTargetQtyUnder
        model.setTotalSupplyTarget(createSumKpi(model, "TotalSupplyTarget",
                model.getSupplyTargetQtyUnderVars()));

        // TotalMinSupply = Σ MinSupplyQtyUnder
        model.setTotalMinSupply(createSumKpi(model, "TotalMinSupply",
                model.getMinSupplyQtyUnderVars()));

        // TotalMaxSupply = Σ MaxSupplyQtyOver
        model.setTotalMaxSupply(createSumKpi(model, "TotalMaxSupply",
                model.getMaxSupplyQtyOverVars()));

        // TotalSalesDemandPriority = Σ SalesDemandQty × Priority
        MPVariable totalSDPri = model.getSolver().makeNumVar(0.0, inf, "TotalSalesDemandPriority");
        MPConstraint sdDef = model.getSolver().makeConstraint(0.0, 0.0, "Def_TotalSDPriority");
        sdDef.setCoefficient(totalSDPri, -1.0);
        for (SalesDemand sd : data.getSalesDemands()) {
            MPVariable sdVar = model.getSalesDemandQtyVars().get(sd.getKey());
            if (sdVar != null) sdDef.setCoefficient(sdVar, sd.getPriority());
        }
        model.setTotalSalesDemandPriority(totalSDPri);

        // 简化版 KPI (固定为 0)
        model.setTotalPostponementPenalty(
                model.getSolver().makeNumVar(0.0, 0.0, "TotalPostponementPenalty"));
        model.setTotalProcessMaxQuantity(
                model.getSolver().makeNumVar(0.0, 0.0, "TotalProcessMaxQty"));
    }

    /** 辅助方法: 创建 SumVar = Σ(values) 的汇总变量 */
    private static MPVariable createSumKpi(MacroPlannerModel model, String name,
                                            Map<String, MPVariable> sourceVars) {
        MPVariable sumVar = model.getSolver().makeNumVar(0.0, MPSolver.infinity(), name);
        MPConstraint def = model.getSolver().makeConstraint(0.0, 0.0, "Def_" + name);
        def.setCoefficient(sumVar, -1.0);
        for (MPVariable v : sourceVars.values()) {
            if (v != null) def.setCoefficient(v, 1.0);
        }
        return sumVar;
    }
}