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;

/**
 * 供应规格约束构建器 (SupplySpecConstraint)
 *
 * <p>跨周期汇总某个供应规格的总供应量, 与目标/最小/最大值比较。
 * 不同于库存规格 (按周期), 供应规格是跨所有周期的总量约束。</p>
 *
 * <h3>数学公式</h3>
 * <pre>
 * 目标供应: ΣPTQty + SupplyTargetQtyUnder ≥ TargetQty
 * 最小供应: ΣPTQty + MinSupplyQtyUnder ≥ MinQty
 * 最大供应: ΣPTQty - MaxSupplyQtyOver ≤ MaxQty
 * </pre>
 */
public class SupplySpecConstraint {

    /**
     * 构建供应规格约束。
     */
    public static void build(MacroPlannerModel model, TestDataBuilder data) {
        Map<String, MPVariable> ptQtyVars = model.getPtQtyVars();
        Map<String, MPVariable> targetUnderVars = model.getSupplyTargetQtyUnderVars();
        Map<String, MPVariable> minUnderVars = model.getMinSupplyQtyUnderVars();
        Map<String, MPVariable> maxOverVars = model.getMaxSupplyQtyOverVars();

        for (SupplySpec spec : data.getSupplySpecs()) {
            // 目标供应
            MPConstraint target = model.getSolver().makeConstraint(
                    spec.getTargetQuantity(), MPSolver.infinity(),
                    "TargetSupply_" + spec.getName());
            addPtQtyTerms(ptQtyVars, spec, data, target);
            MPVariable tu = targetUnderVars.get(spec.getName());
            if (tu != null) target.setCoefficient(tu, 1.0);

            // 最小供应
            MPConstraint min = model.getSolver().makeConstraint(
                    spec.getMinQuantity(), MPSolver.infinity(),
                    "MinSupply_" + spec.getName());
            addPtQtyTerms(ptQtyVars, spec, data, min);
            MPVariable mu = minUnderVars.get(spec.getName());
            if (mu != null) min.setCoefficient(mu, 1.0);

            // 最大供应
            if (spec.hasMaxQuantity()) {
                MPConstraint max = model.getSolver().makeConstraint(
                        -MPSolver.infinity(), spec.getMaxQuantity(),
                        "MaxSupply_" + spec.getName());
                addPtQtyTerms(ptQtyVars, spec, data, max);
                MPVariable mo = maxOverVars.get(spec.getName());
                if (mo != null) max.setCoefficient(mo, -1.0);
            }
        }
    }

    /** 将供应规格关联的所有操作(所有Unit)、所有周期的 PTQty 变量加入约束 */
    private static void addPtQtyTerms(Map<String, MPVariable> ptQtyVars, SupplySpec spec,
                                      TestDataBuilder data, MPConstraint constraint) {
        for (Operation op : spec.getOperations()) {
            for (UnitOperation uo : op.getUnitOperations()) {
                for (Period p : data.getPeriods()) {
                    MPVariable ptVar = ptQtyVars.get(op.ptQtyKey(uo, p.getIndex()));
                    if (ptVar != null) constraint.setCoefficient(ptVar, 1.0);
                }
            }
        }
    }
}