package com.easyagents.flow.core.test;

import com.alibaba.fastjson.JSONObject;
import com.easyagents.flow.core.chain.Chain;
import com.easyagents.flow.core.chain.ChainDefinition;
import com.easyagents.flow.core.chain.Edge;
import com.easyagents.flow.core.chain.Node;
import com.easyagents.flow.core.chain.Parameter;
import com.easyagents.flow.core.chain.RefType;
import com.easyagents.flow.core.chain.repository.ChainDefinitionRepository;
import com.easyagents.flow.core.chain.repository.InMemoryChainStateRepository;
import com.easyagents.flow.core.chain.repository.InMemoryNodeStateRepository;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
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 com.easyagents.flow.core.parser.ChainParser;
import com.easyagents.flow.core.parser.impl.EndNodeParser;
import org.junit.Assert;
import org.junit.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

/**
 * 验证普通节点循环的总次数语义和嵌套计数隔离。
 */
public class GenericNodeLoopCountTest {

    /**
     * 验证默认次数为 1，且零值和超过 300 的值均被拒绝。
     */
    @Test
    public void shouldValidateConfiguredLoopCount() {
        ProbeNode node = new ProbeNode();

        Assert.assertEquals(Node.MIN_LOOP_COUNT, node.getMaxLoopCount());
        assertInvalidLoopCount(node, 0);
        assertInvalidLoopCount(node, Node.MAX_LOOP_COUNT + 1);
    }

    /**
     * 验证 JSON 解析缺省为 1，并拒绝零值、小数和超过上限的配置。
     */
    @Test
    public void shouldValidateParsedLoopCount() {
        ChainParser parser = ChainParser.builder()
                .withDefaultParsers(true)
                .build();
        Node defaultNode = parseLoopNodeConfiguration(parser, null);

        Assert.assertEquals(
                Node.MIN_LOOP_COUNT,
                defaultNode.getMaxLoopCount());
        assertInvalidParsedLoopCount(parser, "0");
        assertInvalidParsedLoopCount(parser, "1.5");
        assertInvalidParsedLoopCount(
                parser,
                String.valueOf(Node.MAX_LOOP_COUNT + 1));
    }

    /**
     * 验证填写 1 执行 1 次，填写 3 执行 3 次。
     */
    @Test
    public void shouldExecuteConfiguredTotalCount() {
        Map<String, Object> once = createExecutor(
                createGenericDefinition("generic-once", 1))
                .execute("generic-once", Collections.emptyMap());
        Map<String, Object> threeTimes = createExecutor(
                createGenericDefinition("generic-three", 3))
                .execute("generic-three", Collections.emptyMap());

        Assert.assertEquals(0, once.get("loopIndex"));
        Assert.assertEquals(2, threeTimes.get("loopIndex"));
    }

    /**
     * 验证显式外层循环每一轮都会让普通内层节点从索引 0 重新开始。
     */
    @Test
    public void shouldResetInnerGenericLoopForEveryOuterIteration() {
        ChainExecutor executor = createExecutor(createNestedDefinition());

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

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

    /**
     * 创建普通循环定义。
     *
     * @param id 定义 ID
     * @param loopCount 总执行次数
     * @return 工作流定义
     */
    private ChainDefinition createGenericDefinition(
            String id, int loopCount) {
        ChainDefinition definition = new ChainDefinition();
        definition.setId(id);

        StartNode start = new StartNode();
        start.setId("start");

        ProbeNode probe = new ProbeNode();
        probe.setId("probe");
        probe.setLoopEnable(true);
        probe.setLoopIntervalMs(0L);
        probe.setMaxLoopCount(loopCount);

        EndNode end = new EndNode();
        end.setId("end");
        Parameter output = new Parameter();
        output.setName("loopIndex");
        output.setRef("probe.loopIndex");
        output.setRefType(RefType.REF);
        end.setOutputDefs(Collections.singletonList(output));

        definition.addNode(start);
        definition.addNode(probe);
        definition.addNode(end);
        definition.addEdge(edge("e1", "start", "probe"));
        definition.addEdge(edge("e2", "probe", "end"));
        return definition;
    }

    /**
     * 创建显式外层循环嵌套普通内层循环的定义。
     *
     * @return 工作流定义
     */
    private ChainDefinition createNestedDefinition() {
        ChainDefinition definition = new ChainDefinition();
        definition.setId("nested-loop-count");

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

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

        ProbeNode probe = new ProbeNode();
        probe.setId("probe");
        probe.setParentId("loop");
        probe.setLoopEnable(true);
        probe.setLoopIntervalMs(0L);
        probe.setMaxLoopCount(2);

        Parameter loopOutput = new Parameter();
        loopOutput.setName("loopIndex");
        loopOutput.setRef("probe.loopIndex");
        loopOutput.setRefType(RefType.REF);
        loop.setOutputDefs(Collections.singletonList(loopOutput));

        EndNode end = new EndNode();
        end.setId("end");
        Parameter result = new Parameter();
        result.setName("loopIndexes");
        result.setRef("loop.loopIndex");
        result.setRefType(RefType.REF);
        end.setOutputDefs(Collections.singletonList(result));

        definition.addNode(start);
        definition.addNode(loop);
        definition.addNode(probe);
        definition.addNode(end);
        definition.addEdge(edge("e1", "start", "loop"));
        definition.addEdge(edge("e2", "loop", "probe"));
        definition.addEdge(edge("e3", "loop", "end"));
        return definition;
    }

    /**
     * 创建执行器。
     *
     * @param definition 工作流定义
     * @return 测试执行器
     */
    private ChainExecutor createExecutor(ChainDefinition definition) {
        return new ChainExecutor(
                new FixedDefinitionRepository(definition),
                new InMemoryChainStateRepository(),
                new InMemoryNodeStateRepository());
    }

    /**
     * 断言设置非法循环次数时抛出参数异常。
     *
     * @param node 测试节点
     * @param loopCount 非法次数
     */
    private void assertInvalidLoopCount(Node node, int loopCount) {
        try {
            node.setMaxLoopCount(loopCount);
            Assert.fail("Expected IllegalArgumentException");
        } catch (IllegalArgumentException expected) {
            Assert.assertTrue(expected.getMessage().contains("maxLoopCount"));
        }
    }

    /**
     * 断言解析非法循环次数时失败。
     *
     * @param parser 工作流解析器
     * @param loopCount 原始次数 JSON 值
     */
    private void assertInvalidParsedLoopCount(
            ChainParser parser, String loopCount) {
        try {
            parseLoopNodeConfiguration(parser, loopCount);
            Assert.fail("Expected IllegalArgumentException");
        } catch (IllegalArgumentException expected) {
            Assert.assertTrue(expected.getMessage().contains("maxLoopCount"));
        }
    }

    /**
     * 解析一个启用普通循环的结束节点。
     *
     * @param parser 工作流解析器
     * @param loopCount 循环次数；为空时省略字段
     * @return 解析后的节点
     */
    private Node parseLoopNodeConfiguration(
            ChainParser parser, String loopCount) {
        JSONObject data = new JSONObject();
        data.put("loopEnable", true);
        if (loopCount != null) {
            data.put("maxLoopCount", loopCount);
        }
        JSONObject nodeJson = new JSONObject();
        nodeJson.put("id", "end");
        nodeJson.put("type", "endNode");
        nodeJson.put("data", data);
        return new EndNodeParser().parse(
                nodeJson, new JSONObject(), parser);
    }

    /**
     * 创建输入参数。
     *
     * @param name 参数名
     * @return 输入参数
     */
    private Parameter inputParameter(String name) {
        Parameter parameter = new Parameter();
        parameter.setName(name);
        parameter.setRefType(RefType.INPUT);
        parameter.setRequired(true);
        return parameter;
    }

    /**
     * 创建连线。
     *
     * @param id 连线 ID
     * @param source 源节点
     * @param target 目标节点
     * @return 连线
     */
    private 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 final class ProbeNode extends BaseNode {

        /**
         * 执行探针节点。
         *
         * @param chain 当前工作流
         * @return 当前零基循环索引
         */
        @Override
        public Map<String, Object> execute(Chain chain) {
            return Collections.singletonMap(
                    "loopIndex",
                    chain.getNodeState(getId()).getLoopCount());
        }
    }

    /**
     * 固定返回测试定义的仓储。
     */
    private static final class FixedDefinitionRepository
            implements ChainDefinitionRepository {

        private final ChainDefinition definition;

        /**
         * 创建定义仓储。
         *
         * @param definition 工作流定义
         */
        private FixedDefinitionRepository(ChainDefinition definition) {
            this.definition = definition;
        }

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