package com.aps.macroplanner.data;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 操作/工艺 (Operation) — 对应 Quintiq 中的 Operation。
 * 表示一个生产操作，可通过多个 {@link UnitOperation} 在不同单元上执行，
 * 每个单元可有不同的产能消耗系数和批次参数。
 *
 * <h3>多产出支持 (联产品 / 副产品)</h3>
 * 一个 Operation 可以有多个 {@link OperationOutput}，对应 Quintiq 中
 * Operation 可以产出到多个 PISP (Product in Stocking Point)。
 * 例如: 炼油工序同时产出汽油和柴油。
 *
 * <h3>向后兼容</h3>
 * {@link #getOutput()} / {@link #getOutputProductId()} / {@link #getOutputSpId()}
 * 返回第一个产出，保持与旧代码的兼容。
 * 新代码应使用 {@link #getOutputs()} 并遍历所有产出。
 */
public class Operation {
    private final String id;
    private final String name;
    private final List<UnitOperation> unitOperations; // 可在多个单元执行, 每个单元参数不同
    private final List<OperationOutput> outputs; // 产出列表 (产品 + 库存点), 对应 Quintiq 中 OperationOutput→PISP
    private final double relativeDuration;      // 产出系数 (每 PTQty 单位产出多少件)
    private final int leadTimeDays;             // 提前期(天数): 生产/采购当天, 入库在 productionDate + leadTimeDays 天

    // ==================== 新构造器 (推荐使用) ====================

    /** 完整构造器 (有产出 + leadTime) */
    public Operation(String id, String name, List<UnitOperation> unitOperations,
                     OperationOutput output, double relativeDuration, int leadTimeDays) {
        this.id = id;
        this.name = name;
        this.unitOperations = new ArrayList<>(unitOperations);
        this.outputs = new ArrayList<>();
        if (output != null) this.outputs.add(output);
        this.relativeDuration = relativeDuration;
        this.leadTimeDays = leadTimeDays;
    }

    /** 有产出, leadTime=0 */
    public Operation(String id, String name, List<UnitOperation> unitOperations,
                     OperationOutput output, double relativeDuration) {
        this(id, name, unitOperations, output, relativeDuration, 0);
    }

    /** 无产出 (Routing 用), 有 leadTime */
    public Operation(String id, String name, List<UnitOperation> unitOperations,
                     double relativeDuration, int leadTimeDays) {
        this(id, name, unitOperations, null, relativeDuration, leadTimeDays);
    }

    /** 无产出, leadTime=0 */
    public Operation(String id, String name, List<UnitOperation> unitOperations,
                     double relativeDuration) {
        this(id, name, unitOperations, null, relativeDuration, 0);
    }

    // ==================== 旧构造器 (保留向后兼容, 标记为 Deprecated) ====================

    /** @deprecated 请使用 {@link #Operation(String, String, List, OperationOutput, double, int)} */
    @Deprecated
    public Operation(String id, String name, String unitId, OperationOutput output,
                     double capacityCoeff, double relativeDuration,
                     boolean hasLotSize, double lotSize, double qtpfactor,
                     int leadTimeDays) {
        this(id, name,
                Arrays.asList(new UnitOperation(unitId, capacityCoeff, hasLotSize, lotSize, qtpfactor)),
                output, relativeDuration, leadTimeDays);
    }

    /** @deprecated 请使用 {@link #Operation(String, String, List, OperationOutput, double)} */
    @Deprecated
    public Operation(String id, String name, String unitId, OperationOutput output,
                     double capacityCoeff, double relativeDuration,
                     boolean hasLotSize, double lotSize, double qtpfactor) {
        this(id, name, unitId, output, capacityCoeff, relativeDuration,
                hasLotSize, lotSize, qtpfactor, 0);
    }

    /** @deprecated 请使用 {@link #Operation(String, String, List, double, int)} */
    @Deprecated
    public Operation(String id, String name, String unitId,
                     double capacityCoeff, double relativeDuration,
                     boolean hasLotSize, double lotSize, double qtpfactor,
                     int leadTimeDays) {
        this(id, name, unitId, null, capacityCoeff, relativeDuration,
                hasLotSize, lotSize, qtpfactor, leadTimeDays);
    }

    /** @deprecated 请使用 {@link #Operation(String, String, List, double)} */
    @Deprecated
    public Operation(String id, String name, String unitId,
                     double capacityCoeff, double relativeDuration,
                     boolean hasLotSize, double lotSize, double qtpfactor) {
        this(id, name, unitId, null, capacityCoeff, relativeDuration,
                hasLotSize, lotSize, qtpfactor, 0);
    }

    // ==================== Getters ====================

    public String getId() { return id; }
    public String getName() { return name; }

    /** 获取所有 UnitOperation (推荐使用)。 */
    public List<UnitOperation> getUnitOperations() {
        return Collections.unmodifiableList(unitOperations);
    }

    /**
     * 生成 PTQty 变量 key: {operationId}_{unitId}_{periodIndex}。
     * 统一 key 格式, 避免在多个文件中硬编码拼接。
     */
    public String ptQtyKey(UnitOperation uo, int periodIndex) {
        return id + "_" + uo.getUnitId() + "_" + periodIndex;
    }

    // === 向后兼容的旧 getter (委托给第一个 UnitOperation) ===

    /** @deprecated 请使用 {@link #getUnitOperations()}.get(0).getUnitId() */
    @Deprecated
    public String getUnitId() { return unitOperations.isEmpty() ? null : unitOperations.get(0).getUnitId(); }

    /** @deprecated 请使用 {@link #getUnitOperations()}.get(0).getCapacityCoeff() */
    @Deprecated
    public double getCapacityCoeff() { return unitOperations.isEmpty() ? 0 : unitOperations.get(0).getCapacityCoeff(); }

    /** @deprecated 请使用 {@link #getUnitOperations()}.get(0).hasLotSize() */
    @Deprecated
    public boolean hasLotSize() { return !unitOperations.isEmpty() && unitOperations.get(0).hasLotSize(); }

    /** @deprecated 请使用 {@link #getUnitOperations()}.get(0).getLotSize() */
    @Deprecated
    public double getLotSize() { return unitOperations.isEmpty() ? 0 : unitOperations.get(0).getLotSize(); }

    /** @deprecated 请使用 {@link #getUnitOperations()}.get(0).getQtpfactor() */
    @Deprecated
    public double getQtpfactor() { return unitOperations.isEmpty() ? 0 : unitOperations.get(0).getQtpfactor(); }

    /**
     * 获取所有产出列表。
     * 对应 Quintiq 中 Operation 的多个 OperationOutput→PISP 关联。
     */
    public List<OperationOutput> getOutputs() {
        return Collections.unmodifiableList(outputs);
    }

    /** 添加产出 (联产品/副产品)。 */
    public void addOutput(OperationOutput output) {
        if (output != null) this.outputs.add(output);
    }

    /**
     * 设置唯一产出 (清除现有产出后添加)。
     * 由 {@link Routing#expand} 调用，自动配置中间 WIP 库存点。
     */
    public void setOutput(OperationOutput output) {
        this.outputs.clear();
        if (output != null) this.outputs.add(output);
    }

    // === 向后兼容的便捷方法 (委托给第一个产出) ===

    /**
     * 获取第一个产出。
     * @deprecated 新代码应使用 {@link #getOutputs()} 遍历所有产出。
     */
    public OperationOutput getOutput() {
        return outputs.isEmpty() ? null : outputs.get(0);
    }

    /**
     * 获取第一个产出的产品 ID。
     * @deprecated 新代码应使用 {@link #getOutputs()} 遍历所有产出。
     */
    public String getOutputProductId() {
        return outputs.isEmpty() ? null : outputs.get(0).getProductId();
    }

    /**
     * 获取第一个产出的库存点 ID。
     * @deprecated 新代码应使用 {@link #getOutputs()} 遍历所有产出。
     */
    public String getOutputSpId() {
        return outputs.isEmpty() ? null : outputs.get(0).getSpId();
    }

    // ==================== 多产出查询方法 ====================

    /**
     * 检查该工序是否产出指定产品 (任一库存点)。
     */
    public boolean producesProduct(String productId) {
        for (OperationOutput oo : outputs) {
            if (oo.getProductId().equals(productId)) return true;
        }
        return false;
    }

    /**
     * 检查该工序是否产出指定产品到指定库存点。
     */
    public boolean producesProductAtSp(String productId, String spId) {
        for (OperationOutput oo : outputs) {
            if (oo.getProductId().equals(productId) && oo.getSpId().equals(spId)) return true;
        }
        return false;
    }

    /**
     * 获取所有产出到指定产品+库存点的产出对象。
     */
    public List<OperationOutput> getOutputsForProductAtSp(String productId, String spId) {
        List<OperationOutput> result = new ArrayList<>();
        for (OperationOutput oo : outputs) {
            if (oo.getProductId().equals(productId) && oo.getSpId().equals(spId)) {
                result.add(oo);
            }
        }
        return result;
    }

    public double getRelativeDuration() { return relativeDuration; }

    public int getLeadTimeDays() { return leadTimeDays; }

    @Override
    public String toString() {
        if (outputs.isEmpty()) return name + "(" + id + ")";
        if (outputs.size() == 1) return name + "(" + id + ") → " + outputs.get(0);
        return name + "(" + id + ") → " + outputs;
    }
}