package com.aps.macroplanner.data;

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

/**
 * 多工序路由测试数据构建器 — 使用 {@link Routing} 自动管理工序间 WIP。
 *
 * <h3>测试场景: 3 工序串行生产同一产品 P, 第一道工序投料消耗原材料 RM</h3>
 * <pre>
 *   原材料 RM → OP_Cut(下料) → WIP_R001_1 → OP_Rough(粗加工) → WIP_R001_2 → OP_Finish(精加工) → SP_FG(成品)
 *                  ↑消耗RM@SP_RM×1
 *
 *   工艺路线 R001 (P生产工艺) 生产产品 P, 最终入库 SP_FG:
 *     工序1: OP_Cut(下料)    → Unit_Cut    → 消耗 RM@SP_RM × 1.0 (投料)
 *     工序2: OP_Rough(粗加工) → Unit_Rough  → 消耗 WIP_R001_1
 *     工序3: OP_Finish(精加工) → Unit_Finish → 产出成品到 SP_FG
 *
 *   工序间 WIP 由 Routing.expand() 自动生成:
 *     - 自动创建 WIP_R001_1 (工序1→2 缓冲区)
 *     - 自动创建 WIP_R001_2 (工序2→3 缓冲区)
 *     - 自动配置 BOM: OP_Rough 消耗 WIP_R001_1, OP_Finish 消耗 WIP_R001_2
 *     - 只有 OP_Finish 产出成品, 中间 WIP 不重复计算
 * </pre>
 *
 * <h3>关键验证点</h3>
 * <pre>
 *   - 原材料 RM 通过 BOM 被 OP_Cut 消耗, 每生产 1 件 P 消耗 1 件 RM
 *   - 3 个工序各自消耗各自设备的产能 (独立计算)
 *   - 只有最后一道工序的产出计入成品供应
 *   - 中间 WIP 被 BOM 约束完全消耗 + RoutingConstraint 强制产量一致
 *   - 用户无需手动配置中间 WIP 库存点、BOM 输入
 * </pre>
 */
public class RoutingTestDataBuilder extends TestDataBuilder {

    public RoutingTestDataBuilder() {
        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 + 原材料 RM ===
        Product prodP = new Product("P", "Product-P");
        Product prodRM = new Product("RM", "RawMaterial");
        products.addAll(Arrays.asList(prodP, prodRM));

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

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

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

        // === 工艺路线: 产品 P 经过 3 道工序生产, 最终入库 SP_FG ===
        // 对应 Quintiq: Routing R001, 关联 Product P 和 StockingPoint SP_FG
        Routing routing = new Routing("R001", "P生产工艺", prodP, spFG);
        routing.addOperation(new Operation("OP_Cut",   "下料",   "Unit_Cut",   1.0, 1.0, false, 0, 1.0));
        routing.addOperation(new Operation("OP_Rough", "粗加工", "Unit_Rough", 1.5, 1.0, false, 0, 1.0));
        routing.addOperation(new Operation("OP_Finish","精加工", "Unit_Finish",2.0, 1.0, false, 0, 1.0));

        operations.addAll(routing.getOperations());
        routings.add(routing);  // 注册到数据构建器, 供 RoutingConstraint 使用

        // === 展开工艺路线: 自动生成 WIP 库存点 + BOM 输入 + 工序产出配置 ===
        // 只有最后工序 (OP_Finish) 产出成品, 中间工序 WIP 自动流转
        RoutingExpansion expansion = routing.expand();
        stockingPoints.addAll(expansion.getStockingPoints());
        productSpMappings.addAll(expansion.getProductSpMappings());
        initialInventories.addAll(expansion.getInitialInventories());
        operationInputs.addAll(expansion.getOperationInputs());

        // === 投料: OP_Cut 消耗原材料 RM@SP_RM (每件 P 消耗 1 件 RM) ===
        operationInputs.add(new OperationInput(
                routing.getOperations().get(0), prodRM, spRM, 1.0));

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

        // === 初始库存 ===
        initialInventories.add(new InitialInventory(prodP, spFG, 0.0));
        initialInventories.add(new InitialInventory(prodRM, spRM, 0.0));

        // === 销售需求: 只在成品库 SP_FG, 每周期 50 ===
        for (Period p : periods) {
            salesDemands.add(new SalesDemand(prodP, spFG, p, 50.0, 1.0));
        }

        // === 库存规格 ===
        for (Period p : periods) {
            inventorySpecs.add(new InventorySpec(prodP, spFG, 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));
        }

        // === 供应规格: 精加工产出 + 原材料采购 ===
        supplySpecs.add(new SupplySpec("Supply-P", 150.0, 100.0, 300.0,
                true, Collections.singletonList(routing.getOperations().get(2))));
        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
                3.0,     // unitCapacityWeight
                8.0,     // supplyTargetWeight
                5.0,     // minSupplyWeight
                5.0,     // maxSupplyWeight
                1.0,     // salesDemandPriorityWeight
                20.0,    // postponementPenaltyWeight
                5.0      // processMaxQuantityWeight
        );
    }
}