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;

/**
 * 产能约束构建器 (CapacityConstraint)
 *
 * <p>确保每个设备在每个周期的产能使用量在 [MinCapacity, MaxCapacity] 范围内。</p>
 *
 * <h3>数学公式</h3>
 * <pre>
 * 最大产能: Σ(PTQty × coeff) - CapacityOverloaded ≤ MaxCapacity
 * 最小产能: Σ(PTQty × coeff) + CapacityNotMet ≥ MinCapacity
 * </pre>
 */
public class CapacityConstraint {

    /**
     * 构建产能上下限约束。
     */
    public static void build(MacroPlannerModel model, TestDataBuilder data) {
        Map<String, MPVariable> ptQtyVars = model.getPtQtyVars();
        Map<String, MPVariable> overloadVars = model.getCapacityOverloadedVars();
        Map<String, MPVariable> notMetVars = model.getCapacityNotMetVars();

        for (UnitPeriod up : data.getUnitPeriods()) {
            // 无限产能单元: 无需产能限制
            if (up.isUnlimited()) continue;

            String capKey = up.getKey();

            // --- 最大产能 ---
            MPConstraint maxCap = model.getSolver().makeConstraint(
                    -MPSolver.infinity(), up.getMaxCapacity(), "MaxCap_" + capKey);
            for (Operation op : data.getOperations()) {
                for (UnitOperation uo : op.getUnitOperations()) {
                    if (!uo.getUnitId().equals(up.getUnitId())) continue;
                    MPVariable ptVar = ptQtyVars.get(op.ptQtyKey(uo, up.getPeriod().getIndex()));
                    if (ptVar != null) maxCap.setCoefficient(ptVar, uo.getCapacityCoeff());
                }
            }
            MPVariable ov = overloadVars.get(capKey);
            if (ov != null) maxCap.setCoefficient(ov, -1.0);

            // --- 最小产能 ---
            if (up.hasMinCapacity()) {
                MPConstraint minCap = model.getSolver().makeConstraint(
                        up.getMinCapacity(), MPSolver.infinity(), "MinCap_" + capKey);
                for (Operation op : data.getOperations()) {
                    for (UnitOperation uo : op.getUnitOperations()) {
                        if (!uo.getUnitId().equals(up.getUnitId())) continue;
                        MPVariable ptVar = ptQtyVars.get(op.ptQtyKey(uo, up.getPeriod().getIndex()));
                        if (ptVar != null) minCap.setCoefficient(ptVar, uo.getCapacityCoeff());
                    }
                }
                MPVariable nm = notMetVars.get(capKey);
                if (nm != null) minCap.setCoefficient(nm, 1.0);
            }
        }
    }
}