package com.aps.macroplanner.data;

import java.util.*;

/**
 * 综合测试数据构建器 — 覆盖所有约束场景的 3 级 BOM 供应链优化实例
 *
 * <h2>场景概览</h2>
 * <pre>
 * 物料流 (3级BOM):
 *   OP3 → ProductC@SP3 ──┬──→ (被 OP1 消耗, 因子 0.5)
 *                         │
 *   OP2 → ProductB@SP2 ───┤──→ (被 OP1 消耗, 因子 2.0)
 *                         │
 *   OP1 → ProductA@SP1 ───┘──→ (外部销售需求, P3 高峰 120)
 *
 * 设备:
 *   Unit1 (max=200) ← OP1(coeff=1.0) + OP3(coeff=0.8) 争抢
 *   Unit2 (max=120, min=30) ← OP2 专用
 * </pre>
 *
 * <h2>各约束覆盖情况</h2>
 * <pre>
 * 约束类型         | 触发场景                                    | 预期触发的松弛变量
 * ─────────────────┼───────────────────────────────────────────┼────────────────────
 * 物料平衡         | 3级BOM链式传递, 5周期, 初始库存40/30/60    | DemandSlack
 * BOM依赖需求      | OP1双输入(PB×2.0, PC×0.5), PC共享消耗      | OperationDemandQty
 * 产能(最大)       | Unit1 200被OP1+OP3争抢, P3需求高峰        | CapacityOverloaded
 * 产能(最小)       | Unit2 最小产能30 强制OP2生产               | CapacityNotMet
 * 库存(最小)       | 安全库存10, 可能被突破                     | MinInvQtyUnder
 * 库存(最大)       | PC 最大库存80 紧约束                       | MaxInvQtyOver
 * 库存(目标)       | 目标库存80/40/50, 无法完全达成             | InvQtyUnderTarget
 * 供应(目标)       | 目标偏高(380/200/250)                      | SupplyTargetQtyUnder
 * 供应(最小)       | 最小供应可满足                             | (可能不触发)
 * 供应(最大)       | 最大供应宽松                               | (可能不触发)
 * 批次大小         | OP1 lotSize=100, OP2 lotSize=50            | PTLotSizeOver/Under
 * ─────────────────┴───────────────────────────────────────────┴────────────────────
 * </pre>
 */
public class ComprehensiveTestDataBuilder extends TestDataBuilder {

    // 产品引用 (供 build() 内部使用)
    private Product prodA, prodB, prodC;
    private StockingPoint sp1, sp2, sp3;
    private Operation op1, op2, op3;

    /** 构造器 — 调用父类 protected 构造器跳过自动初始化, 然后自行构建 */
    public ComprehensiveTestDataBuilder() {
        super(true);
        build();
    }

    @Override
    protected void build() {
        createPeriods();
        createProducts();
        createStockingPoints();
        createOperations();
        createBomInputs();
        createUnitPeriods();
        createInitialInventory();
        createSalesDemands();
        createInventorySpecs();
        createSupplySpecs();
        createKpiWeights();
    }

    // ==================== 1. 周期 (5个周期) ====================

    private void createPeriods() {
        for (int i = 0; i < 5; i++) {
            periods.add(new Period(i, "P" + (i + 1)));
        }
    }

    // ==================== 2. 产品 ====================

    private void createProducts() {
        prodA = new Product("PA", "ProductA-成品");
        prodB = new Product("PB", "ProductB-半成品");
        prodC = new Product("PC", "ProductC-原材料");
        products.addAll(Arrays.asList(prodA, prodB, prodC));
    }

    // ==================== 3. 库存点 ====================

    private void createStockingPoints() {
        sp1 = new StockingPoint("SP1", "FG-成品仓");
        sp2 = new StockingPoint("SP2", "SF-半成品仓");
        sp3 = new StockingPoint("SP3", "RM-原料仓");
        stockingPoints.addAll(Arrays.asList(sp1, sp2, sp3));
        // 产品→库存点映射
        productSpMappings.add(new ProductSpMapping(prodA, sp1));
        productSpMappings.add(new ProductSpMapping(prodB, sp2));
        productSpMappings.add(new ProductSpMapping(prodC, sp3));
    }

    // ==================== 4. 操作 ====================

    private void createOperations() {
        // OP1: 装配 ProductA, 使用 Unit1, 产能系数 1.0, 批次 100
        op1 = new Operation("OP1", "装配-ProductA", "Unit1",
                new OperationOutput(prodA, sp1), 1.0, 1.0, true, 100.0, 1.0);

        // OP2: 加工 ProductB, 使用 Unit2, 产能系数 1.0, 批次 50
        op2 = new Operation("OP2", "加工-ProductB", "Unit2",
                new OperationOutput(prodB, sp2), 1.0, 1.0, true, 50.0, 1.0);

        // OP3: 备料 ProductC, 使用 Unit1, 产能系数 0.8, 无批次
        //     (与 OP1 共享 Unit1, 争抢产能)
        op3 = new Operation("OP3", "备料-ProductC", "Unit1",
                new OperationOutput(prodC, sp3), 0.8, 1.0, false, 0.0, 1.0);

        operations.addAll(Arrays.asList(op1, op2, op3));
    }

    // ==================== 5. BOM / 操作输入物料 ====================

    private void createBomInputs() {
        // OP1 (装配成品A) 每生产 1 个 ProductA, 消耗:
        //   - 2.0 个 ProductB@SP2 (半成品)
        //   - 0.5 个 ProductC@SP3 (原材料)
        operationInputs.add(new OperationInput(op1, prodB, sp2, 2.0));
        operationInputs.add(new OperationInput(op1, prodC, sp3, 0.5));

        // OP2 (加工半成品B) 每生产 1 个 ProductB, 消耗:
        //   - 1.5 个 ProductC@SP3 (原材料)
        // 注意: PC 同时被 OP1(0.5×) 和 OP2(1.5×) 消耗, 测试共享输入
        operationInputs.add(new OperationInput(op2, prodC, sp3, 1.5));
    }

    // ==================== 6. 单元周期 ====================

    private void createUnitPeriods() {
        // Unit1: 最大产能 200/周期, 无最小产能 (OP1 和 OP3 共享)
        // Unit2: 最大产能 120/周期, 最小产能 30 (OP2 专用, 强制最低生产)
        for (Period p : periods) {
            unitPeriods.add(new UnitPeriod("Unit1", p, 0.0, 200.0, false));
            unitPeriods.add(new UnitPeriod("Unit2", p, 30.0, 120.0, true));
        }
    }

    // ==================== 7. 初始库存 ====================

    private void createInitialInventory() {
        initialInventories.add(new InitialInventory(prodA, sp1, 40.0));
        initialInventories.add(new InitialInventory(prodB, sp2, 30.0));
        initialInventories.add(new InitialInventory(prodC, sp3, 60.0));
    }

    // ==================== 8. 销售需求 ====================

    private void createSalesDemands() {
        // ProductA 成品 @ SP1: 需求波动 (P3 高峰 120!)
        double[] paDemands = {60, 80, 120, 80, 60};
        for (int i = 0; i < 5; i++) {
            salesDemands.add(new SalesDemand(prodA, sp1, periods.get(i),
                    paDemands[i], 1.0));
        }

        // ProductB 半成品 @ SP2: 少量外部销售 (高优先级)
        for (Period p : periods) {
            salesDemands.add(new SalesDemand(prodB, sp2, p, 10.0, 2.0));
        }

        // ProductC 原材料 @ SP3: 无外部销售, 仅被 BOM 消耗
    }

    // ==================== 9. 库存规格 ====================

    private void createInventorySpecs() {
        // ProductA @ SP1: 目标库存 = 3天安全库存, 最小库存 = 2天安全库存, 最大库存 = 200 (绝对数量)
        //   含义: 期末库存应覆盖未来3天的需求, 且至少覆盖2天, 最多不超过200个
        for (Period p : periods) {
            inventorySpecs.add(new InventorySpec(prodA, sp1, p,
                    0.0, 0.0, 200.0, true, true, true,         // 绝对数量: 全部启用 (但值用天数覆盖)
                    3.0, 2.0, 0.0, true, true, false));        // 天数: target=3天, min=2天
        }

        // ProductB @ SP2: 目标 40, 最小 10, 最大 120 (全部绝对数量 — 传统模式)
        for (Period p : periods) {
            inventorySpecs.add(new InventorySpec(prodB, sp2, p,
                    40.0, 10.0, 120.0, true, true, true));
        }

        // ProductC @ SP3: 目标 50, 最小 10, 最大 80 (全部绝对数量 — 紧约束)
        for (Period p : periods) {
            inventorySpecs.add(new InventorySpec(prodC, sp3, p,
                    50.0, 10.0, 80.0, true, true, true));
        }
    }

    // ==================== 10. 供应规格 ====================

    private void createSupplySpecs() {
        // Supply-A: OP1 总产量目标 380, 最小 280, 最大 500
        // 5周期 PA 需求合计 400, 目标 380 偏高, 预期触发 SupplyTargetQtyUnder
        supplySpecs.add(new SupplySpec("Supply-A", 380.0, 280.0, 500.0,
                true, Collections.singletonList(op1)));

        // Supply-B: OP2 总产量目标 200, 最小 150, 最大 300
        supplySpecs.add(new SupplySpec("Supply-B", 200.0, 150.0, 300.0,
                true, Collections.singletonList(op2)));

        // Supply-C: OP3 总产量目标 250, 最小 180, 最大 350
        supplySpecs.add(new SupplySpec("Supply-C", 250.0, 180.0, 350.0,
                true, Collections.singletonList(op3)));
    }

    // ==================== 11. KPI 权重 ====================

    private void createKpiWeights() {
        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
        );
    }
}