package com.aps.macroplanner.constraint;

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

import java.util.Map;

/**
 * 需求满足量汇总约束 (DemandFulfillmentInPISPIP)
 *
 * <p>定义每个 PISPIP 的总需求满足量 = 销售需求 + BOM 依赖需求。
 * 这个变量是安全库存天数计算的基础。</p>
 *
 * <h3>数学公式</h3>
 * <pre>
 * DemandFulfillmentInPISPIP = Σ SalesDemandQty + Σ OperationDemandQty
 *
 * 即: 每个 PISPIP 的总需求满足量 = 所有销售需求满足量 + 所有 BOM 消耗量
 * </pre>
 *
 * <h3>用途</h3>
 * 安全库存天数约束: InvQty ≥ Σ(ratio × DemandFulfillment)
 * 即: 期末库存必须覆盖未来N天的需求满足量
 */
public class DemandFulfillmentConstraint {

    /**
     * 构建 DemandFulfillmentInPISPIP 定义约束。
     */
    public static void build(MacroPlannerModel model, TestDataBuilder data) {
        Map<String, MPVariable> dfVars = model.getDemandFulfillmentVars();
        Map<String, MPVariable> sdVars = model.getSalesDemandQtyVars();
        Map<String, MPVariable> opDemandVars = model.getOperationDemandQtyVars();

        for (Product prod : data.getProducts()) {
            for (StockingPoint sp : data.getStockingPointsForProduct(prod.getId())) {

                for (Period p : data.getPeriods()) {
                    String key = prod.getId() + "_" + sp.getId() + "_" + p.getIndex();

                    // DemandFulfillment = SalesDemandQty + OperationDemandQty
                    MPConstraint c = model.getSolver().makeConstraint(
                            0.0, 0.0, "DFulfill_" + key);

                    // + DemandFulfillment 变量
                    MPVariable dfVar = dfVars.get(key);
                    if (dfVar != null) c.setCoefficient(dfVar, -1.0);

                    // + SalesDemandQty
                    for (SalesDemand sd : data.getSalesDemands()) {
                        if (sd.getProduct().getId().equals(prod.getId())
                                && sd.getStockingPoint().getId().equals(sp.getId())
                                && sd.getPeriod().getIndex() == p.getIndex()) {
                            MPVariable sdVar = sdVars.get(sd.getKey());
                            if (sdVar != null) c.setCoefficient(sdVar, 1.0);
                        }
                    }

                    // + OperationDemandQty (BOM 消耗)
                    for (OperationInput input : data.getOperationInputs()) {
                        if (input.getInputProduct().getId().equals(prod.getId())
                                && input.getInputSp().getId().equals(sp.getId())) {
                            String opKey = input.getKey() + "_" + p.getIndex();
                            MPVariable odVar = opDemandVars.get(opKey);
                            if (odVar != null) c.setCoefficient(odVar, 1.0);
                        }
                    }
                }
            }
        }
    }
}