package com.aps.macroplanner.data;

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

/**
 * 多级 BOM + 多成品 + 共享半成品 测试数据构建器。
 *
 * <h3>BOM 结构</h3>
 * <pre>
 *   P1 ──消耗──→ S1 × 2.0 + R1 × 3.0
 *   P2 ──消耗──→ S1 × 1.0
 *   S1 ──消耗──→ R2 × 2.0
 *
 *   物料分级:
 *     成品 (Finished):   P1, P2
 *     半成品 (Semi):     S1 (被 P1 和 P2 共享)
 *     原材料 (Raw):      R1 (P1直接消耗), R2 (S1消耗)
 * </pre>
 *
 * <h3>推导需求 (每周期)</h3>
 * <pre>
 *   P1 销售: 40 → S1需求: 40×2=80,  R1需求: 40×3=120
 *   P2 销售: 30 → S1需求: 30×1=30
 *   S1 总需求: 80+30=110 → R2需求: 110×2=220
 * </pre>
 *
 * <h3>关键验证点</h3>
 * <pre>
 *   - 共享半成品 S1 的总生产量 = P1生产量×2.0 + P2生产量×1.0
 *   - 原材料 R2 消耗 = S1生产量×2.0
 *   - 原材料 R1 消耗 = P1生产量×3.0
 *   - 所有需求满足 (需求缺口=0)
 *   - 多级 BOM 展开正确
 * </pre>
 */
public class MultiLevelBomTestDataBuilder extends TestDataBuilder {

    @Override
    protected void build() {
        // === 周期: 3 天 ===
        periods.add(new Period(0, "Day1", LocalDate.of(2026, 8, 7)));
        periods.add(new Period(1, "Day2", LocalDate.of(2026, 8, 8)));
        periods.add(new Period(2, "Day3", LocalDate.of(2026, 8, 9)));

        // === 产品: 成品 P1, P2 + 半成品 S1 + 原材料 R1, R2 ===
        Product prodP1 = new Product("P1", "成品P1");
        Product prodP2 = new Product("P2", "成品P2");
        Product prodS1 = new Product("S1", "半成品S1");
        Product prodR1 = new Product("R1", "原材料R1");
        Product prodR2 = new Product("R2", "原材料R2");
        products.addAll(Arrays.asList(prodP1, prodP2, prodS1, prodR1, prodR2));

        // === 库存点: 每种产品一个库存点 ===
        StockingPoint spP1 = new StockingPoint("SP_P1", "P1成品库");
        StockingPoint spP2 = new StockingPoint("SP_P2", "P2成品库");
        StockingPoint spSemi = new StockingPoint("SP_Semi", "半成品库");
        StockingPoint spR1 = new StockingPoint("SP_R1", "R1原材料库");
        StockingPoint spR2 = new StockingPoint("SP_R2", "R2原材料库");
        stockingPoints.addAll(Arrays.asList(spP1, spP2, spSemi, spR1, spR2));

        // === 产品→库存点映射 ===
        productSpMappings.add(new ProductSpMapping(prodP1, spP1));
        productSpMappings.add(new ProductSpMapping(prodP2, spP2));
        productSpMappings.add(new ProductSpMapping(prodS1, spSemi));
        productSpMappings.add(new ProductSpMapping(prodR1, spR1));
        productSpMappings.add(new ProductSpMapping(prodR2, spR2));

        // === 生产工序: 成品 + 半成品 ===
        // P1: 消耗 S1×2.0 + R1×3.0, 产出 P1@SP_P1
        Operation opP1 = new Operation("OP_P1", "生产P1", "Unit_P1",
                new OperationOutput(prodP1, spP1), 1.0, 1.0, false, 0, 1.0);
        // P2: 消耗 S1×1.0, 产出 P2@SP_P2
        Operation opP2 = new Operation("OP_P2", "生产P2", "Unit_P2",
                new OperationOutput(prodP2, spP2), 1.0, 1.0, false, 0, 1.0);
        // S1: 消耗 R2×2.0, 产出 S1@SP_Semi
        Operation opS1 = new Operation("OP_S1", "生产S1", "Unit_S1",
                new OperationOutput(prodS1, spSemi), 1.0, 1.0, false, 0, 1.0);
        operations.addAll(Arrays.asList(opP1, opP2, opS1));

        // === 原材料采购工序 ===
        Operation opProcureR1 = new Operation("OP_Procure_R1", "采购R1", "Unit_R1",
                new OperationOutput(prodR1, spR1), 0.5, 1.0, false, 0, 1.0);
        Operation opProcureR2 = new Operation("OP_Procure_R2", "采购R2", "Unit_R2",
                new OperationOutput(prodR2, spR2), 0.5, 1.0, false, 0, 1.0);
        operations.addAll(Arrays.asList(opProcureR1, opProcureR2));

        // === BOM 投料: P1 消耗 S1×2.0 + R1×3.0 ===
        operationInputs.add(new OperationInput(opP1, prodS1, spSemi, 2.0));
        operationInputs.add(new OperationInput(opP1, prodR1, spR1, 3.0));

        // === BOM 投料: P2 消耗 S1×1.0 ===
        operationInputs.add(new OperationInput(opP2, prodS1, spSemi, 1.0));

        // === BOM 投料: S1 消耗 R2×2.0 ===
        operationInputs.add(new OperationInput(opS1, prodR2, spR2, 2.0));

        // === 设备产能: 每个设备 200h/周期 ===
        for (Period p : periods) {
            unitPeriods.add(new UnitPeriod("Unit_P1", p, 0.0, 200.0, false));
            unitPeriods.add(new UnitPeriod("Unit_P2", p, 0.0, 200.0, false));
            unitPeriods.add(new UnitPeriod("Unit_S1", p, 0.0, 200.0, false));
            unitPeriods.add(new UnitPeriod("Unit_R1", p, 0.0, 200.0, false));
            unitPeriods.add(new UnitPeriod("Unit_R2", p, 0.0, 200.0, false));
        }

        // === 初始库存: 全部从 0 开始 ===
        initialInventories.add(new InitialInventory(prodP1, spP1, 0.0));
        initialInventories.add(new InitialInventory(prodP2, spP2, 0.0));
        initialInventories.add(new InitialInventory(prodS1, spSemi, 0.0));
        initialInventories.add(new InitialInventory(prodR1, spR1, 0.0));
        initialInventories.add(new InitialInventory(prodR2, spR2, 0.0));

        // === 销售需求: P1=40/周期, P2=30/周期 ===
        for (Period p : periods) {
            salesDemands.add(new SalesDemand(prodP1, spP1, p, 40.0, 1.0));
            salesDemands.add(new SalesDemand(prodP2, spP2, p, 30.0, 1.0));
        }

        // === 库存规格: 目标库存 ===
        for (Period p : periods) {
            inventorySpecs.add(new InventorySpec(prodP1, spP1, p,
                    80.0, 10.0, 200.0, true, true, true));
            inventorySpecs.add(new InventorySpec(prodP2, spP2, p,
                    60.0, 10.0, 200.0, true, true, true));
            inventorySpecs.add(new InventorySpec(prodS1, spSemi, p,
                    100.0, 10.0, 300.0, true, true, true));
            inventorySpecs.add(new InventorySpec(prodR1, spR1, p,
                    80.0, 10.0, 500.0, true, true, true));
            inventorySpecs.add(new InventorySpec(prodR2, spR2, p,
                    80.0, 10.0, 500.0, true, true, true));
        }

        // === 供应规格: 成品+半成品各一个, 原材料各一个 ===
        supplySpecs.add(new SupplySpec("Supply-P1", 120.0, 80.0, 300.0,
                true, Collections.singletonList(opP1)));
        supplySpecs.add(new SupplySpec("Supply-P2", 90.0, 60.0, 300.0,
                true, Collections.singletonList(opP2)));
        supplySpecs.add(new SupplySpec("Supply-S1", 300.0, 200.0, 500.0,
                true, Collections.singletonList(opS1)));
        supplySpecs.add(new SupplySpec("Supply-R1", 400.0, 200.0, 800.0,
                true, Collections.singletonList(opProcureR1)));
        supplySpecs.add(new SupplySpec("Supply-R2", 600.0, 400.0, 1000.0,
                true, Collections.singletonList(opProcureR2)));

        // === KPI 权重 (必须显式初始化, 因为覆写了 build() 不调用 super) ===
        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
        );
    }
}