package com.aps.macroplanner.data;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.Collections;

/**
 * 联产品/副产品多工序路由测试数据构建器。
 *
 * <h3>测试场景: 化工反应工序同时产出主产品 P 和副产品 ByP</h3>
 * <pre>
 *   原材料 RM ─→ OP_Reaction(反应) ─→ P@WIP_R_P_1 ─→ OP_Purify(提纯) ─→ P@SP_FG(成品)
 *                   │ 消耗 RM@SP_RM×2.0
 *                   │
 *                   └─→ ByP@SP_ByProduct (副产品, 联产品产出)
 *
 *   工艺路线 R_P (P主生产工艺) 生产产品 P, 最终入库 SP_FG:
 *     工序1: OP_Reaction(反应) → Unit_Reactor → 消耗 RM@SP_RM×2.0
 *             └ 联产品产出: ByP@SP_ByProduct (预配置, expand() 保留)
 *             └ WIP 产出:   P@WIP_R_P_1 (expand() 自动生成)
 *     工序2: OP_Purify(提纯)  → Unit_Purify  → 消耗 P@WIP_R_P_1, 产出 P@SP_FG
 * </pre>
 *
 * <h3>关键验证点</h3>
 * <ol>
 *   <li>OP_Reaction 有 2 个产出: P@WIP_R_P_1 (路由自动) + ByP@SP_ByProduct (联产品)</li>
 *   <li>RoutingConstraint 确保 PTQty[Reaction] = PTQty[Purify]</li>
 *   <li>ByP 的产出量 = PTQty[Reaction], 与主产品 P 同步</li>
 *   <li>P@SP_FG 物料平衡: 流入 (OP_Purify) = 流出 (Sales)</li>
 *   <li>ByP@SP_ByProduct 物料平衡: 流入 (OP_Reaction 联产品) = 流出 (Sales) + 期末库存</li>
 *   <li>P@WIP_R_P_1 物料平衡: 流入 (OP_Reaction) = 流出 (OP_Purify 消耗)</li>
 * </ol>
 */
public class CoProductTestDataBuilder extends TestDataBuilder {

    // 保存关键对象引用, 用于验证
    private Product prodP, prodByP, prodRM;
    private StockingPoint spFG, spByProduct, spRM;
    private Operation opReaction, opPurify, opProcureRM;
    private Routing routingP;

    public CoProductTestDataBuilder() {
        super(true);  // 跳过父类默认 build
        build();
    }

    @Override
    protected void build() {
        LocalDate baseDate = LocalDate.of(2026, 1, 5);

        // === 3 个周期 ===
        Period p1 = new Period(0, "P1", baseDate);
        Period p2 = new Period(1, "P2", baseDate.plusDays(1));
        Period p3 = new Period(2, "P3", baseDate.plusDays(2));
        periods.addAll(Arrays.asList(p1, p2, p3));

        // === 产品: 主产品 P + 副产品 ByP + 原材料 RM ===
        prodP = new Product("P", "主产品");
        prodByP = new Product("ByP", "副产品");
        prodRM = new Product("RM", "原材料");
        products.addAll(Arrays.asList(prodP, prodByP, prodRM));

        // === 库存点: 成品库 + 副产品库 + 原材料库 ===
        spFG = new StockingPoint("SP_FG", "成品库");
        spByProduct = new StockingPoint("SP_ByProduct", "副产品库");
        spRM = new StockingPoint("SP_RM", "原材料库");
        stockingPoints.addAll(Arrays.asList(spFG, spByProduct, spRM));

        // === 产品→库存点映射 ===
        productSpMappings.add(new ProductSpMapping(prodP, spFG));
        productSpMappings.add(new ProductSpMapping(prodByP, spByProduct));
        productSpMappings.add(new ProductSpMapping(prodRM, spRM));

        // === 关键: 预配置联产品产出 (在 Routing.expand() 之前) ===
        // OP_Reaction 除了产出 P@WIP (由路由自动生成), 还产出 ByP@SP_ByProduct (联产品)
        opReaction = new Operation("OP_Reaction", "反应", "Unit_Reactor",
                1.0, 1.0, false, 0, 1.0);  // 无产出构造器
        opReaction.addOutput(new OperationOutput(prodByP, spByProduct));
        // 此时 opReaction 已有 1 个产出: ByP@SP_ByProduct

        // OP_Purify 消耗前道 WIP, 产出成品 P@SP_FG
        opPurify = new Operation("OP_Purify", "提纯", "Unit_Purify",
                1.0, 1.0, false, 0, 1.0);  // 无产出构造器

        // === 工艺路线: P 主产品 2 步生产 ===
        routingP = new Routing("R_P", "P主生产工艺", prodP, spFG);
        routingP.addOperation(opReaction);
        routingP.addOperation(opPurify);
        operations.addAll(routingP.getOperations());
        routings.add(routingP);

        // === 展开工艺路线 ===
        // expand() 使用 addOutput() 追加产出, 不会覆盖预配置的 ByP@SP_ByProduct
        // 展开后 opReaction 有 2 个产出: ByP@SP_ByProduct(联产品) + P@WIP_R_P_1(路由)
        //           opPurify   有 1 个产出: P@SP_FG(路由)
        RoutingExpansion expansion = routingP.expand();
        stockingPoints.addAll(expansion.getStockingPoints());
        productSpMappings.addAll(expansion.getProductSpMappings());
        initialInventories.addAll(expansion.getInitialInventories());
        operationInputs.addAll(expansion.getOperationInputs());

        // === 投料: OP_Reaction 消耗 RM@SP_RM (每件消耗 2 件原材料) ===
        operationInputs.add(new OperationInput(
                routingP.getOperations().get(0), prodRM, spRM, 2.0));

        // === 原材料采购工序 ===
        opProcureRM = new Operation("OP_Procure_RM", "采购原材料", "Unit_RM",
                new OperationOutput(prodRM, spRM), 0.5, 1.0, false, 0, 1.0);
        operations.add(opProcureRM);

        // === 设备产能 ===
        for (Period p : periods) {
            unitPeriods.add(new UnitPeriod("Unit_Reactor", p, 0.0, 100.0, false));
        }
        for (Period p : periods) {
            unitPeriods.add(new UnitPeriod("Unit_Purify", p, 0.0, 100.0, false));
        }
        for (Period p : periods) {
            unitPeriods.add(new UnitPeriod("Unit_RM", p, 0.0, 200.0, false));
        }

        // === 初始库存 (全部为 0) ===
        initialInventories.add(new InitialInventory(prodP, spFG, 0.0));
        initialInventories.add(new InitialInventory(prodByP, spByProduct, 0.0));
        initialInventories.add(new InitialInventory(prodRM, spRM, 50.0));  // 给一些初始库存

        // === 销售需求 ===
        // 主产品 P: 50/周期
        for (Period p : periods) {
            salesDemands.add(new SalesDemand(prodP, spFG, p, 50.0, 1.0));
        }
        // 副产品 ByP: 30/周期 (少于 P 的产量, 会有剩余库存)
        for (Period p : periods) {
            salesDemands.add(new SalesDemand(prodByP, spByProduct, p, 30.0, 1.0));
        }

        // === 库存规格 ===
        for (Period p : periods) {
            inventorySpecs.add(new InventorySpec(prodP, spFG, p,
                    100.0, 10.0, 200.0, true, true, true));
        }
        for (Period p : periods) {
            inventorySpecs.add(new InventorySpec(prodByP, spByProduct, p,
                    80.0, 10.0, 200.0, true, true, true));
        }
        for (Period p : periods) {
            inventorySpecs.add(new InventorySpec(prodRM, spRM, p,
                    80.0, 10.0, 500.0, true, true, true));
        }

        // === 供应规格 ===
        // 主产品 P 由提纯工序产出
        supplySpecs.add(new SupplySpec("Supply-P", 150.0, 100.0, 300.0,
                true, Collections.singletonList(routingP.getOperations().get(1))));
        // 副产品 ByP 由反应工序联产品产出
        supplySpecs.add(new SupplySpec("Supply-ByP", 150.0, 100.0, 300.0,
                true, Collections.singletonList(routingP.getOperations().get(0))));
        // 原材料 RM 采购
        supplySpecs.add(new SupplySpec("Supply-RM", 200.0, 100.0, 500.0,
                true, Collections.singletonList(opProcureRM)));

        // === KPI 权重 ===
        kpiWeights = new KPIWeights(
                100.0,   // fulfillmentWeight
                10.0,    // lotSizeWeight
                5.0,     // maxInventoryLevelWeight
                5.0,     // minInventoryLevelWeight
                8.0,     // targetInventoryLevelWeight
                20.0,    // unitCapacityWeight
                8.0,     // supplyTargetWeight
                5.0,     // minSupplyWeight
                5.0,     // maxSupplyWeight
                1.0,     // salesDemandPriorityWeight
                20.0,    // postponementPenaltyWeight
                5.0      // processMaxQuantityWeight
        );
    }

    // ==================== 便捷访问方法 (用于验证) ====================

    public Product getProdP() { return prodP; }
    public Product getProdByP() { return prodByP; }
    public Product getProdRM() { return prodRM; }
    public StockingPoint getSpFG() { return spFG; }
    public StockingPoint getSpByProduct() { return spByProduct; }
    public StockingPoint getSpRM() { return spRM; }
    public Operation getOpReaction() { return opReaction; }
    public Operation getOpPurify() { return opPurify; }
    public Routing getRoutingP() { return routingP; }
}