package com.easyagents.flow.core.chain.runtime;

import java.io.Serializable;

/**
 * 工作流执行的全局资源保护预算。
 * <p>
 * 所有默认值均为宽松的失控保护值。小于等于 {@code 0} 的配置表示关闭对应保护，
 * 节点自身的循环次数、退出条件和重试配置仍按原有语义优先生效。
 */
public final class ExecutionBudget implements Serializable {
    private static final long serialVersionUID = 1L;


    public static final long DEFAULT_MAX_ITERATIONS = 100_000L;
    /**
     * 缺省不限制墙钟时长，避免人工确认或长期挂起时间被误计为执行耗时。
     */
    public static final long DEFAULT_MAX_DURATION_MILLIS = 0L;
    public static final long DEFAULT_MAX_CHILD_EXECUTIONS = 1_000_000L;
    public static final long DEFAULT_MAX_ACCUMULATED_BYTES = 512L * 1024L * 1024L;
    public static final int DEFAULT_MAX_NESTED_DEPTH = 32;
    /**
     * 热状态硬限制缺省关闭。循环历史已通过引用隔离，开启限制时由部署方按实际负载设置，
     * 避免估算误差改变既有业务语义。
     */
    public static final long DEFAULT_MAX_HOT_STATE_BYTES = 0L;

    private final long maxIterations;
    private final long maxDurationMillis;
    private final long maxChildExecutions;
    private final long maxAccumulatedBytes;
    private final int maxNestedDepth;
    private final long maxHotStateBytes;

    /**
     * 创建执行预算。
     *
     * @param maxIterations       单循环最大迭代次数
     * @param maxDurationMillis   单实例最大运行毫秒数
     * @param maxChildExecutions  单实例最大节点执行次数
     * @param maxAccumulatedBytes 单循环最大累计结果字节数
     * @param maxNestedDepth      最大循环嵌套深度
     * @param maxHotStateBytes    单实例热状态建议最大字节数
     */
    public ExecutionBudget(long maxIterations,
                           long maxDurationMillis,
                           long maxChildExecutions,
                           long maxAccumulatedBytes,
                           int maxNestedDepth,
                           long maxHotStateBytes) {
        this.maxIterations = maxIterations;
        this.maxDurationMillis = maxDurationMillis;
        this.maxChildExecutions = maxChildExecutions;
        this.maxAccumulatedBytes = maxAccumulatedBytes;
        this.maxNestedDepth = maxNestedDepth;
        this.maxHotStateBytes = maxHotStateBytes;
    }

    /**
     * 创建使用宽松缺省值的执行预算。
     *
     * @return 默认执行预算
     */
    public static ExecutionBudget defaults() {
        return new ExecutionBudget(
                DEFAULT_MAX_ITERATIONS,
                DEFAULT_MAX_DURATION_MILLIS,
                DEFAULT_MAX_CHILD_EXECUTIONS,
                DEFAULT_MAX_ACCUMULATED_BYTES,
                DEFAULT_MAX_NESTED_DEPTH,
                DEFAULT_MAX_HOT_STATE_BYTES);
    }

    public long getMaxIterations() {
        return maxIterations;
    }

    public long getMaxDurationMillis() {
        return maxDurationMillis;
    }

    public long getMaxChildExecutions() {
        return maxChildExecutions;
    }

    public long getMaxAccumulatedBytes() {
        return maxAccumulatedBytes;
    }

    public int getMaxNestedDepth() {
        return maxNestedDepth;
    }

    public long getMaxHotStateBytes() {
        return maxHotStateBytes;
    }

    /**
     * 校验循环迭代总数。
     *
     * @param nodeId     循环节点 ID
     * @param iterations 计划迭代次数
     * @throws ExecutionBudgetExceededException 超过启用的迭代预算时抛出
     */
    public void checkIterations(String nodeId, long iterations) {
        if (maxIterations > 0 && iterations > maxIterations) {
            throw new ExecutionBudgetExceededException(
                    "Loop iteration budget exceeded for node " + nodeId
                            + ": " + iterations + " > " + maxIterations);
        }
    }

    /**
     * 校验循环嵌套深度。
     *
     * @param nodeId 节点 ID
     * @param depth  当前深度
     * @throws ExecutionBudgetExceededException 超过启用的深度预算时抛出
     */
    public void checkNestedDepth(String nodeId, int depth) {
        if (maxNestedDepth > 0 && depth > maxNestedDepth) {
            throw new ExecutionBudgetExceededException(
                    "Loop nested depth budget exceeded for node " + nodeId
                            + ": " + depth + " > " + maxNestedDepth);
        }
    }

    /**
     * 校验循环累计结果大小。
     *
     * @param nodeId           循环节点 ID
     * @param accumulatedBytes 当前累计估算字节数
     * @throws ExecutionBudgetExceededException 超过启用的累计结果预算时抛出
     */
    public void checkAccumulatedBytes(String nodeId, long accumulatedBytes) {
        if (maxAccumulatedBytes > 0 && accumulatedBytes > maxAccumulatedBytes) {
            throw new ExecutionBudgetExceededException(
                    "Loop accumulated result budget exceeded for node " + nodeId
                            + ": " + accumulatedBytes + " > " + maxAccumulatedBytes);
        }
    }

    /**
     * 校验单实例节点执行次数。
     *
     * @param executions 当前节点执行次数
     * @throws ExecutionBudgetExceededException 超过启用的执行预算时抛出
     */
    public void checkChildExecutions(long executions) {
        if (maxChildExecutions > 0 && executions > maxChildExecutions) {
            throw new ExecutionBudgetExceededException(
                    "Workflow child execution budget exceeded: "
                            + executions + " > " + maxChildExecutions);
        }
    }

    /**
     * 校验单实例运行时长。
     *
     * @param startedAtMillis 实例开始时间
     * @param nowMillis       当前时间
     * @throws ExecutionBudgetExceededException 超过启用的时长预算时抛出
     */
    public void checkDuration(long startedAtMillis, long nowMillis) {
        if (maxDurationMillis > 0
                && startedAtMillis > 0
                && nowMillis - startedAtMillis > maxDurationMillis) {
            throw new ExecutionBudgetExceededException(
                    "Workflow duration budget exceeded: "
                            + (nowMillis - startedAtMillis) + "ms > " + maxDurationMillis + "ms");
        }
    }

    /**
     * 校验工作流热状态估算大小。
     *
     * @param estimatedBytes 当前热状态估算字节数
     * @throws ExecutionBudgetExceededException 超过启用的热状态预算时抛出
     */
    public void checkHotStateBytes(long estimatedBytes) {
        if (maxHotStateBytes > 0 && estimatedBytes > maxHotStateBytes) {
            throw new ExecutionBudgetExceededException(
                    "Workflow hot state budget exceeded: "
                            + estimatedBytes + " > " + maxHotStateBytes);
        }
    }
}
