package com.easyagents.flow.core.test;

import com.easyagents.flow.core.chain.*;
import com.easyagents.flow.core.chain.repository.ChainDefinitionRepository;
import com.easyagents.flow.core.chain.repository.InMemoryChainStateRepository;
import com.easyagents.flow.core.chain.repository.InMemoryLoopResultRepository;
import com.easyagents.flow.core.chain.repository.InMemoryNodeStateRepository;
import com.easyagents.flow.core.chain.repository.LoopInputReference;
import com.easyagents.flow.core.chain.repository.NodeStateField;
import com.easyagents.flow.core.chain.repository.NodeStateRepository;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import com.easyagents.flow.core.chain.runtime.ExecutionBudget;
import com.easyagents.flow.core.chain.runtime.ExecutionBudgetExceededException;
import com.easyagents.flow.core.chain.runtime.InMemoryTriggerStore;
import com.easyagents.flow.core.chain.runtime.TriggerScheduler;
import com.easyagents.flow.core.chain.event.NodeEndEvent;
import com.easyagents.flow.core.chain.event.NodeStartEvent;
import com.easyagents.flow.core.node.BaseNode;
import com.easyagents.flow.core.node.EndNode;
import com.easyagents.flow.core.node.LoopNode;
import com.easyagents.flow.core.node.StartNode;
import org.junit.Assert;
import org.junit.Test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.AbstractList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.Executors;

/**
 * 验证循环体内可以读取父循环节点上一轮的最新输出。
 */
public class LoopNodeProgressContextTest {

    @Test
    public void shouldExposeLatestLoopOutputInsideLoopBody() {
        ChainDefinition definition = createDefinition();
        ChainExecutor executor = createExecutor(definition);
        Map<String, Object> variables = new HashMap<>();
        variables.put("times", 2);

        Map<String, Object> resultMap = executor.execute("loop-progress-test", variables);

        Assert.assertEquals(Arrays.asList("1", "2"), resultMap.get("result"));
    }

    /**
     * 验证循环节点跨子触发器完成时复用同一稳定业务尝试键。
     */
    @Test
    public void shouldKeepLoopExecutionAttemptKeyStable() {
        ChainDefinition definition =
                createDefinition();
        ChainExecutor executor =
                createExecutor(definition);
        java.util.concurrent.atomic.AtomicReference<String>
                startedKey =
                new java.util.concurrent.atomic.AtomicReference<>();
        java.util.concurrent.atomic.AtomicReference<String>
                endedKey =
                new java.util.concurrent.atomic.AtomicReference<>();
        java.util.concurrent.atomic.AtomicReference<NodeStatus>
                endedStatus =
                new java.util.concurrent.atomic.AtomicReference<>();
        java.util.concurrent.atomic.AtomicReference<Throwable>
                endedError =
                new java.util.concurrent.atomic.AtomicReference<>();
        executor.addEventListener(
                NodeStartEvent.class,
                (event, chain) -> {
                    NodeStartEvent startEvent =
                            (NodeStartEvent) event;
                    if ("loop".equals(
                            startEvent.getNode().getId())) {
                        startedKey.set(
                                startEvent
                                        .getExecutionAttemptKey());
                    }
                });
        executor.addEventListener(
                NodeEndEvent.class,
                (event, chain) -> {
                    NodeEndEvent endEvent =
                            (NodeEndEvent) event;
                    if ("loop".equals(
                            endEvent.getNode().getId())) {
                        // 模拟下一轮执行已抢先覆盖可变节点状态。
                        chain.updateNodeStateSafely(
                                "loop",
                                state -> {
                                    state.setExecutionAttemptKey(
                                            "next-attempt");
                                    state.setStatus(
                                            NodeStatus.RUNNING);
                                    state.setError(
                                            new ExceptionSummary(
                                                    new IllegalStateException(
                                                            "next-error")));
                                    return EnumSet.of(
                                            NodeStateField.STATUS,
                                            NodeStateField.ERROR,
                                            NodeStateField
                                                    .EXECUTION_ATTEMPT_KEY);
                                });
                        endedKey.set(
                                endEvent
                                        .getExecutionAttemptKey());
                        endedStatus.set(
                                endEvent.getStatus());
                        endedError.set(
                                endEvent.getError());
                    }
                });

        executor.execute(
                "loop-attempt-key-test",
                Collections.singletonMap(
                        "times", 3));

        Assert.assertNotNull(startedKey.get());
        Assert.assertEquals(
                startedKey.get(),
                endedKey.get());
        Assert.assertEquals(
                NodeStatus.SUCCEEDED,
                endedStatus.get());
        Assert.assertNull(
                endedError.get());
    }

    /**
     * 验证普通 Iterable 只物化一次，后续循环不会从头重复遍历。
     */
    @Test
    public void shouldMaterializeNonListIterableOnlyOnce() {
        ChainDefinition definition = createDefinition();
        ChainExecutor executor = createExecutor(definition);
        OneShotIterable iterable = new OneShotIterable();
        Map<String, Object> variables = new HashMap<>();
        variables.put("times", iterable);

        Map<String, Object> resultMap = executor.execute("loop-progress-test", variables);

        Assert.assertEquals(Arrays.asList("1", "2", "3"), resultMap.get("result"));
        Assert.assertEquals(1, iterable.getIteratorCount());
    }

    /**
     * 验证超过直接索引阈值的 List 首轮物化后不再回读原集合。
     */
    @Test
    public void shouldMaterializeListOnlyOnce() {
        ChainDefinition definition = createDefinition();
        ChainExecutor executor = createExecutor(definition);
        Integer[] values = new Integer[128];
        Arrays.fill(values, 1);
        CountingList list = new CountingList(values);

        Map<String, Object> resultMap = executor.execute(
                "loop-list-test", Collections.singletonMap("times", list));

        Assert.assertEquals(128, ((java.util.List<?>) resultMap.get("result")).size());
        Assert.assertTrue(
                "list input should not be re-read for every iteration",
                list.getReadCount() <= values.length * 5);
    }

    /**
     * 验证循环直接消费上游已分页物化的轻量引用，不先还原完整列表。
     */
    @Test
    public void shouldConsumePreMaterializedInputReference() {
        ChainDefinition definition = createDefinition();
        InMemoryLoopResultRepository loopRepository =
                new InMemoryLoopResultRepository();
        String resultId = "loop-reference-input";
        loopRepository.storeInput(
                resultId, Arrays.asList(10, 20, 30));
        TriggerScheduler scheduler =
                new TriggerScheduler(
                        new InMemoryTriggerStore(),
                        Executors.newScheduledThreadPool(2),
                        Executors.newFixedThreadPool(2),
                        1_000L);
        try {
            ChainExecutor executor = new ChainExecutor(
                    new FixedDefinitionRepository(
                            definition),
                    new InMemoryChainStateRepository(),
                    new InMemoryNodeStateRepository(),
                    loopRepository,
                    scheduler,
                    ExecutionBudget.defaults());

            Map<String, Object> resultMap =
                    executor.execute(
                            "loop-reference-test",
                            Collections.singletonMap(
                                    "times",
                                    new LoopInputReference(
                                            resultId, 3)));

            Assert.assertEquals(
                    Arrays.asList("1", "2", "3"),
                    resultMap.get("result"));
        } finally {
            scheduler.shutdown();
        }
    }

    /**
     * 验证对象数组和基础类型数组均可作为循环输入。
     */
    @Test
    public void shouldSupportArrayInputs() {
        ChainDefinition definition = createDefinition();
        ChainExecutor executor = createExecutor(definition);

        Map<String, Object> objectArrayResult = executor.execute(
                "loop-object-array-test",
                Collections.singletonMap("times", new String[]{"a", "b"}));
        Map<String, Object> primitiveArrayResult = executor.execute(
                "loop-primitive-array-test",
                Collections.singletonMap("times", new int[]{1, 2, 3}));

        Assert.assertEquals(Arrays.asList("1", "2"), objectArrayResult.get("result"));
        Assert.assertEquals(
                Arrays.asList("1", "2", "3"), primitiveArrayResult.get("result"));
    }

    /**
     * 验证次数和数组同时存在时只遍历数组前 N 项。
     */
    @Test
    public void shouldLimitArrayLoopByConfiguredCount() {
        ChainExecutor executor = createExecutor(
                createDualInputDefinition(true, true));
        Map<String, Object> variables = new HashMap<>();
        variables.put("count", 2);
        variables.put("items", Arrays.asList("a", "b", "c"));

        Map<String, Object> result = executor.execute(
                "loop-dual-input-test", variables);

        Assert.assertEquals(
                Arrays.asList("a", "b"),
                result.get("result"));
        Assert.assertEquals(
                Arrays.asList("a", "b", "c"),
                result.get("original"));
    }

    /**
     * 验证次数大于数组长度时只处理现有元素。
     */
    @Test
    public void shouldStopAtArrayLengthWhenCountIsLarger() {
        ChainExecutor executor = createExecutor(
                createDualInputDefinition(true, true));
        Map<String, Object> variables = new HashMap<>();
        variables.put("count", 10);
        variables.put("items", Arrays.asList("a", "b", "c"));

        Map<String, Object> result = executor.execute(
                "loop-dual-input-short-array-test", variables);

        Assert.assertEquals(
                Arrays.asList("a", "b", "c"),
                result.get("result"));
    }

    /**
     * 验证仅配置次数时继续把当前序号作为 loopItem。
     */
    @Test
    public void shouldSupportCountOnlyInput() {
        ChainExecutor executor = createExecutor(
                createDualInputDefinition(true, false));

        Map<String, Object> result = executor.execute(
                "loop-count-only-test",
                Collections.singletonMap("count", 3));

        Assert.assertEquals(
                Arrays.asList(0, 1, 2),
                result.get("result"));
    }

    /**
     * 验证配置数组后即使次数合法也拒绝非数组运行值。
     */
    @Test
    public void shouldRejectNonArrayItemsWhenCountIsPresent() {
        ChainExecutor executor = createExecutor(
                createDualInputDefinition(true, true));
        Map<String, Object> variables = new HashMap<>();
        variables.put("count", 2);
        variables.put("items", "not-an-array");

        assertLoopFailure(
                executor,
                "loop-invalid-items-test",
                variables,
                RuntimeException.class);
    }

    /**
     * 验证大数组可通过次数限制只处理前 3 项，且上游数组保持完整。
     */
    @Test
    public void shouldAllowLargeArrayWhenCountLimitsPrefix() {
        ChainExecutor executor = createExecutor(
                createDualInputDefinition(true, true));
        java.util.List<Integer> items = new java.util.ArrayList<>();
        for (int index = 0;
             index < Node.MAX_LOOP_COUNT + 1;
             index++) {
            items.add(index);
        }
        Map<String, Object> variables = new HashMap<>();
        variables.put("count", 3);
        variables.put("items", items);

        Map<String, Object> result = executor.execute(
                "loop-large-prefix-test", variables);

        Assert.assertEquals(
                Arrays.asList(0, 1, 2),
                result.get("result"));
        Assert.assertEquals(
                Node.MAX_LOOP_COUNT + 1,
                ((java.util.List<?>) result.get("original")).size());
    }

    /**
     * 验证显式循环节点接受 300 次，并拒绝 301 次的数值输入。
     */
    @Test
    public void shouldEnforceNumericLoopLimit() {
        ChainExecutor maximumExecutor = createExecutor(createDefinition());
        Map<String, Object> maximumResult = maximumExecutor.execute(
                "loop-maximum-test",
                Collections.singletonMap("times", Node.MAX_LOOP_COUNT));

        Assert.assertEquals(
                Node.MAX_LOOP_COUNT,
                ((java.util.List<?>) maximumResult.get("result")).size());

        ChainExecutor exceededExecutor = createExecutor(createDefinition());
        assertLoopFailure(
                exceededExecutor,
                "loop-exceeded-test",
                Collections.singletonMap(
                        "times", Node.MAX_LOOP_COUNT + 1),
                IllegalArgumentException.class);
    }

    /**
     * 验证未知大小 Iterable 在物化第 301 个元素前终止。
     */
    @Test
    public void shouldStopOversizedIterableDuringMaterialization() {
        ChainExecutor executor = createExecutor(createDefinition());

        assertLoopFailure(
                executor,
                "loop-iterable-exceeded-test",
                Collections.singletonMap(
                        "times",
                        new RangeIterable(
                                Node.MAX_LOOP_COUNT + 1)),
                ExecutionBudgetExceededException.class);
    }

    /**
     * 验证大循环累计结果不会进入每轮持久化的节点热状态。
     */
    @Test
    public void shouldKeepNodeStateCompactWhenCollectingLargeLoopResult() {
        ChainDefinition definition = createDefinition();
        TrackingNodeStateRepository nodeStateRepository = new TrackingNodeStateRepository();
        ChainExecutor executor = new ChainExecutor(new FixedDefinitionRepository(definition),
                new InMemoryChainStateRepository(),
                nodeStateRepository);
        Map<String, Object> variables = new HashMap<>();
        variables.put("times", Node.MAX_LOOP_COUNT);

        Map<String, Object> resultMap = executor.execute("loop-progress-test", variables);

        @SuppressWarnings("unchecked")
        java.util.List<String> result = (java.util.List<String>) resultMap.get("result");
        Assert.assertEquals(Node.MAX_LOOP_COUNT, result.size());
        Assert.assertEquals("1", result.get(0));
        Assert.assertEquals(
                String.valueOf(Node.MAX_LOOP_COUNT),
                result.get(Node.MAX_LOOP_COUNT - 1));
        Assert.assertTrue("loop node hot state should remain bounded",
                nodeStateRepository.getMaxSerializedBytes() < 16 * 1024);
    }

    /**
     * 验证循环体存在多个直属分支时，必须等待全部分支完成后再推进下一轮。
     */
    @Test
    public void shouldWaitForAllDirectLoopBranchesBeforeAdvancing() {
        ChainDefinition definition = createMultiBranchDefinition();
        ChainExecutor executor = createExecutor(definition);

        Map<String, Object> resultMap = executor.execute(
                "loop-multi-branch-test", Collections.singletonMap("times", 3));

        Assert.assertEquals(Arrays.asList("a0", "a1", "a2"), resultMap.get("a"));
        Assert.assertEquals(Arrays.asList("b0", "b1", "b2"), resultMap.get("b"));
    }

    /**
     * 创建循环进度测试定义。
     *
     * @return 工作流定义
     */
    private ChainDefinition createDefinition() {
        ChainDefinition definition = new ChainDefinition();
        definition.setId("loop-progress-test");

        StartNode startNode = new StartNode();
        startNode.setId("start");
        startNode.setName("开始节点");
        startNode.setParameters(Collections.singletonList(inputParameter("times")));

        LoopNode loopNode = new LoopNode();
        loopNode.setId("loop");
        loopNode.setName("循环节点");
        Parameter loopVar = new Parameter();
        loopVar.setName("times");
        loopVar.setRef("times");
        loopVar.setRefType(RefType.REF);
        loopNode.setLoopVar(loopVar);

        AccumulatorNode accumulatorNode = new AccumulatorNode("loop");
        accumulatorNode.setId("acc");
        accumulatorNode.setName("累计节点");
        accumulatorNode.setParentId("loop");

        Parameter loopOutput = new Parameter();
        loopOutput.setName("current");
        loopOutput.setRef("acc.current");
        loopOutput.setRefType(RefType.REF);
        loopNode.setOutputDefs(Collections.singletonList(loopOutput));

        EndNode endNode = new EndNode();
        endNode.setId("end");
        endNode.setName("结束节点");
        Parameter result = new Parameter();
        result.setName("result");
        result.setRef("loop.current");
        result.setRefType(RefType.REF);
        endNode.setOutputDefs(Collections.singletonList(result));

        definition.addNode(startNode);
        definition.addNode(loopNode);
        definition.addNode(accumulatorNode);
        definition.addNode(endNode);
        definition.addEdge(edge("e1", "start", "loop"));
        definition.addEdge(edge("e2", "loop", "acc"));
        definition.addEdge(edge("e3", "loop", "end"));
        return definition;
    }

    /**
     * 创建使用新双输入结构的循环定义。
     *
     * @param includeCount 是否配置循环次数
     * @param includeItems 是否配置输入数组
     * @return 工作流定义
     */
    private ChainDefinition createDualInputDefinition(
            boolean includeCount, boolean includeItems) {
        ChainDefinition definition = new ChainDefinition();
        definition.setId("loop-dual-input-test");

        StartNode startNode = new StartNode();
        startNode.setId("start");
        java.util.List<Parameter> inputs =
                new java.util.ArrayList<>();
        if (includeCount) {
            inputs.add(inputParameter("count"));
        }
        if (includeItems) {
            inputs.add(inputParameter("items"));
        }
        startNode.setParameters(inputs);

        LoopNode loopNode = new LoopNode();
        loopNode.setId("loop");
        loopNode.setName("循环节点");
        if (includeCount) {
            Parameter count = new Parameter();
            count.setName("count");
            count.setRef("count");
            count.setRefType(RefType.REF);
            count.setDataType(DataType.Number);
            loopNode.setLoopCount(count);
        }
        if (includeItems) {
            Parameter items = new Parameter();
            items.setName("items");
            items.setRef("items");
            items.setRefType(RefType.REF);
            items.setDataType(DataType.Array);
            loopNode.setLoopItems(items);
        }

        ItemCollectorNode collectorNode =
                new ItemCollectorNode("loop");
        collectorNode.setId("collector");
        collectorNode.setParentId("loop");

        Parameter loopOutput = new Parameter();
        loopOutput.setName("value");
        loopOutput.setRef("collector.value");
        loopOutput.setRefType(RefType.REF);
        loopNode.setOutputDefs(
                Collections.singletonList(loopOutput));

        EndNode endNode = new EndNode();
        endNode.setId("end");
        Parameter result = new Parameter();
        result.setName("result");
        result.setRef("loop.value");
        result.setRefType(RefType.REF);
        java.util.List<Parameter> outputs =
                new java.util.ArrayList<>();
        outputs.add(result);
        if (includeItems) {
            Parameter original = new Parameter();
            original.setName("original");
            original.setRef("items");
            original.setRefType(RefType.REF);
            outputs.add(original);
        }
        endNode.setOutputDefs(outputs);

        definition.addNode(startNode);
        definition.addNode(loopNode);
        definition.addNode(collectorNode);
        definition.addNode(endNode);
        definition.addEdge(edge("e1", "start", "loop"));
        definition.addEdge(edge("e2", "loop", "collector"));
        definition.addEdge(edge("e3", "loop", "end"));
        return definition;
    }

    /**
     * 创建包含两个并行直属循环分支的定义。
     *
     * @return 工作流定义
     */
    private ChainDefinition createMultiBranchDefinition() {
        ChainDefinition definition = new ChainDefinition();
        definition.setId("loop-multi-branch-test");

        StartNode startNode = new StartNode();
        startNode.setId("start");
        startNode.setParameters(Collections.singletonList(inputParameter("times")));

        LoopNode loopNode = new LoopNode();
        loopNode.setId("loop");
        Parameter loopVar = new Parameter();
        loopVar.setName("times");
        loopVar.setRef("times");
        loopVar.setRefType(RefType.REF);
        loopNode.setLoopVar(loopVar);

        BranchNode branchA = new BranchNode("a", 0L);
        branchA.setId("branch-a");
        branchA.setParentId("loop");
        BranchNode branchB = new BranchNode("b", 20L);
        branchB.setId("branch-b");
        branchB.setParentId("loop");

        Parameter outputA = new Parameter();
        outputA.setName("a");
        outputA.setRef("branch-a.value");
        outputA.setRefType(RefType.REF);
        Parameter outputB = new Parameter();
        outputB.setName("b");
        outputB.setRef("branch-b.value");
        outputB.setRefType(RefType.REF);
        loopNode.setOutputDefs(Arrays.asList(outputA, outputB));

        EndNode endNode = new EndNode();
        endNode.setId("end");
        Parameter resultA = new Parameter();
        resultA.setName("a");
        resultA.setRef("loop.a");
        resultA.setRefType(RefType.REF);
        Parameter resultB = new Parameter();
        resultB.setName("b");
        resultB.setRef("loop.b");
        resultB.setRefType(RefType.REF);
        endNode.setOutputDefs(Arrays.asList(resultA, resultB));

        definition.addNode(startNode);
        definition.addNode(loopNode);
        definition.addNode(branchA);
        definition.addNode(branchB);
        definition.addNode(endNode);
        definition.addEdge(edge("e1", "start", "loop"));
        definition.addEdge(edge("e2", "loop", "branch-a"));
        definition.addEdge(edge("e3", "loop", "branch-b"));
        definition.addEdge(edge("e4", "loop", "end"));
        return definition;
    }

    /**
     * 创建使用内存状态仓储的测试执行器。
     *
     * @param definition 工作流定义
     * @return 测试执行器
     */
    private ChainExecutor createExecutor(ChainDefinition definition) {
        return new ChainExecutor(new FixedDefinitionRepository(definition),
                new InMemoryChainStateRepository(),
                new InMemoryNodeStateRepository());
    }

    /**
     * 断言循环节点通过结束事件报告指定异常类型。
     *
     * @param executor 测试执行器
     * @param definitionId 定义 ID
     * @param variables 输入变量
     * @param expectedType 期望异常类型
     */
    private static void assertLoopFailure(
            ChainExecutor executor,
            String definitionId,
            Map<String, Object> variables,
            Class<? extends Throwable> expectedType) {
        AtomicReference<Throwable> nodeError = new AtomicReference<>();
        executor.addEventListener(
                NodeEndEvent.class,
                (event, chain) -> {
                    NodeEndEvent endEvent = (NodeEndEvent) event;
                    if ("loop".equals(endEvent.getNode().getId())
                            && endEvent.getError() != null) {
                        nodeError.set(endEvent.getError());
                    }
                });
        try {
            executor.execute(definitionId, variables);
            Assert.fail("Expected exception: " + expectedType.getSimpleName());
        } catch (RuntimeException expected) {
            Assert.assertNotNull("Loop node error event is missing", nodeError.get());
            Assert.assertTrue(
                    "Unexpected loop node error: " + nodeError.get(),
                    expectedType.isInstance(nodeError.get()));
        }
    }

    private static Parameter inputParameter(String name) {
        Parameter parameter = new Parameter();
        parameter.setName(name);
        parameter.setRefType(RefType.INPUT);
        parameter.setRequired(true);
        return parameter;
    }

    private static Edge edge(String id, String source, String target) {
        Edge edge = new Edge();
        edge.setId(id);
        edge.setSource(source);
        edge.setTarget(target);
        return edge;
    }

    private static class FixedDefinitionRepository implements ChainDefinitionRepository {
        private final ChainDefinition definition;

        private FixedDefinitionRepository(ChainDefinition definition) {
            this.definition = definition;
        }

        @Override
        public ChainDefinition getChainDefinitionById(String id) {
            return definition;
        }
    }

    /**
     * 第二次获取迭代器时直接失败，用于识别重复遍历。
     */
    private static class OneShotIterable implements Iterable<Integer> {
        private final AtomicInteger iteratorCount = new AtomicInteger();

        /**
         * 获取唯一可用的迭代器。
         *
         * @return 测试迭代器
         */
        @Override
        public Iterator<Integer> iterator() {
            if (iteratorCount.incrementAndGet() > 1) {
                throw new IllegalStateException("Iterable must not be traversed more than once");
            }
            return Arrays.asList(10, 20, 30).iterator();
        }

        /**
         * 获取迭代器创建次数。
         *
         * @return 迭代器创建次数
         */
        private int getIteratorCount() {
            return iteratorCount.get();
        }
    }

    /**
     * 生成指定数量元素且无法提前获知大小的 Iterable。
     */
    private static final class RangeIterable implements Iterable<Integer> {

        private final int itemCount;

        /**
         * 创建范围输入。
         *
         * @param itemCount 元素数量
         */
        private RangeIterable(int itemCount) {
            this.itemCount = itemCount;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Iterator<Integer> iterator() {
            return new Iterator<Integer>() {
                private int index;

                @Override
                public boolean hasNext() {
                    return index < itemCount;
                }

                @Override
                public Integer next() {
                    if (!hasNext()) {
                        throw new java.util.NoSuchElementException();
                    }
                    return index++;
                }
            };
        }
    }

    /**
     * 记录元素读取次数的列表。
     */
    private static final class CountingList extends AbstractList<Integer> {

        private final java.util.List<Integer> values;
        private final AtomicInteger readCount = new AtomicInteger();

        /**
         * 创建计数列表。
         *
         * @param values 列表元素
         */
        private CountingList(Integer... values) {
            this.values = Arrays.asList(values);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Integer get(int index) {
            readCount.incrementAndGet();
            return values.get(index);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int size() {
            return values.size();
        }

        /**
         * 获取元素读取次数。
         *
         * @return 元素读取次数
         */
        private int getReadCount() {
            return readCount.get();
        }
    }

    /**
     * 记录节点状态序列化体积的测试仓储。
     */
    private static final class TrackingNodeStateRepository implements NodeStateRepository {

        private final InMemoryNodeStateRepository delegate = new InMemoryNodeStateRepository();
        private final AtomicInteger maxSerializedBytes = new AtomicInteger();

        /**
         * {@inheritDoc}
         */
        @Override
        public NodeState load(String instanceId, String nodeId) {
            return delegate.load(instanceId, nodeId);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public boolean tryUpdate(NodeState newState, EnumSet<NodeStateField> fields, long chainStateVersion) {
            maxSerializedBytes.accumulateAndGet(serializedSize(newState), Math::max);
            return delegate.tryUpdate(newState, fields, chainStateVersion);
        }

        /**
         * 获取观测到的最大序列化字节数。
         *
         * @return 最大序列化字节数
         */
        private int getMaxSerializedBytes() {
            return maxSerializedBytes.get();
        }

        /**
         * 计算节点状态的 Java 序列化字节数。
         *
         * @param state 节点状态
         * @return 序列化字节数
         */
        private int serializedSize(NodeState state) {
            try (ByteArrayOutputStream output = new ByteArrayOutputStream();
                 ObjectOutputStream objectOutput = new ObjectOutputStream(output)) {
                objectOutput.writeObject(state);
                objectOutput.flush();
                return output.size();
            } catch (IOException error) {
                throw new IllegalStateException("Failed to serialize node state", error);
            }
        }
    }

    /**
     * 每一轮都读取父循环节点上一轮的 current，再计算新的结果。
     */
    private static class AccumulatorNode extends BaseNode {
        private final String loopNodeId;

        private AccumulatorNode(String loopNodeId) {
            this.loopNodeId = loopNodeId;
        }

        @Override
        public Map<String, Object> execute(Chain chain) {
            Object previous = chain.getState().resolveValue(loopNodeId + ".current");
            Object indexValue = chain.getState().resolveValue(loopNodeId + ".index");
            int index = indexValue instanceof Number
                    ? ((Number) indexValue).intValue()
                    : Integer.parseInt(String.valueOf(indexValue));

            if (index == 0) {
                Assert.assertEquals("", previous);
            } else if (index == 1) {
                Assert.assertEquals("1", previous);
            }

            String current = index == 0
                    ? "1"
                    : String.valueOf(Integer.parseInt(String.valueOf(previous)) + 1);

            Map<String, Object> result = new HashMap<>();
            result.put("current", current);
            return result;
        }
    }

    /**
     * 输出当前 loopItem 的测试节点。
     */
    private static class ItemCollectorNode extends BaseNode {
        private final String loopNodeId;

        /**
         * 创建当前项收集节点。
         *
         * @param loopNodeId 父循环节点 ID
         */
        private ItemCollectorNode(String loopNodeId) {
            this.loopNodeId = loopNodeId;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Map<String, Object> execute(Chain chain) {
            Object item = chain.getState().resolveValue(
                    loopNodeId + ".loopItem");
            return Collections.singletonMap("value", item);
        }
    }

    /**
     * 输出当前循环序号的测试分支。
     */
    private static class BranchNode extends BaseNode {
        private final String prefix;
        private final long delayMs;

        private BranchNode(String prefix, long delayMs) {
            this.prefix = prefix;
            this.delayMs = delayMs;
        }

        @Override
        public Map<String, Object> execute(Chain chain) {
            if (delayMs > 0) {
                try {
                    Thread.sleep(delayMs);
                } catch (InterruptedException error) {
                    Thread.currentThread().interrupt();
                    throw new IllegalStateException("Branch execution interrupted", error);
                }
            }
            Object index = chain.getState().resolveValue("loop.index");
            return Collections.singletonMap("value", prefix + index);
        }
    }
}
