package com.easyagents.flow.core.test;

import com.easyagents.flow.core.chain.Chain;
import com.easyagents.flow.core.chain.ChainDefinition;
import com.easyagents.flow.core.chain.ChainState;
import com.easyagents.flow.core.chain.NodeState;
import com.easyagents.flow.core.chain.repository.InMemoryChainStateRepository;
import com.easyagents.flow.core.chain.repository.InMemoryLoopResultRepository;
import com.easyagents.flow.core.node.LoopNode;
import org.junit.Assert;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.List;
import java.util.Map;

/**
 * 校验工作流 Java 序列化状态的滚动升级兼容性。
 */
public class LegacySerializationCompatibilityTest {

    private static final String LEGACY_LOOP_CONTEXT =
            "rO0ABXNyADJjb20uZWFzeWFnZW50cy5mbG93LmNvcmUubm9kZS5Mb29wTm9k"
                    + "ZSRMb29wQ29udGV4dEj5b620EJczAgACSQAMY3VycmVudEluZGV4TAAJc3Vi"
                    + "UmVzdWx0dAAPTGphdmEvdXRpbC9NYXA7eHAAAAADc3IAF2phdmEudXRpbC5M"
                    + "aW5rZWRIYXNoTWFwNMBOXBBswPsCAAFaAAthY2Nlc3NPcmRlcnhyABFqYXZh"
                    + "LnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVz"
                    + "aG9sZHhwP0AAAAAAAAx3CAAAABAAAAABdAAFdmFsdWVzcgATamF2YS51dGls"
                    + "LkFycmF5TGlzdHiB0h2Zx2GdAwABSQAEc2l6ZXhwAAAAAncEAAAAAnQAAWF0"
                    + "AAFieHgA";

    /**
     * 保护历史类的默认序列化 UID，避免缓存状态在滚动升级时失效。
     */
    @Test
    public void shouldKeepLegacySerialVersionUids() {
        Assert.assertEquals(-7958235553581638052L,
                ObjectStreamClass.lookup(ChainState.class).getSerialVersionUID());
        Assert.assertEquals(-6727481826462129573L,
                ObjectStreamClass.lookup(NodeState.class).getSerialVersionUID());
        Assert.assertEquals(5258356831772776243L,
                ObjectStreamClass.lookup(LoopNode.LoopContext.class).getSerialVersionUID());
    }

    /**
     * 旧 LoopContext 中的内嵌结果应惰性迁移到分块结果仓储。
     *
     * @throws Exception 反序列化或反射调用失败
     */
    @Test
    @SuppressWarnings("unchecked")
    public void shouldMigrateLegacyLoopResultWithoutDataLoss() throws Exception {
        LoopNode.LoopContext context;
        byte[] bytes = Base64.getDecoder().decode(LEGACY_LOOP_CONTEXT);
        try (ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
            context = (LoopNode.LoopContext) input.readObject();
        }
        Assert.assertEquals(3, context.getCurrentIndex());
        Assert.assertEquals(List.of("a", "b"), context.getSubResult().get("value"));
        Assert.assertNull(context.getResultId());

        InMemoryLoopResultRepository resultRepository = new InMemoryLoopResultRepository();
        Chain chain = new Chain(new ChainDefinition(), "legacy-chain");
        chain.setChainStateRepository(new InMemoryChainStateRepository());
        chain.setLoopResultRepository(resultRepository);

        LoopNode loopNode = new LoopNode();
        Method migrate = LoopNode.class.getDeclaredMethod(
                "migrateLegacyResult", Chain.class, LoopNode.LoopContext.class);
        migrate.setAccessible(true);
        migrate.invoke(loopNode, chain, context);

        Assert.assertNotNull(context.getResultId());
        Assert.assertNull(context.getSubResult());
        Map<String, Object> migrated =
                resultRepository.load(context.getResultId(), 2, List.of("value"));
        Assert.assertEquals(List.of("a", "b"), migrated.get("value"));
    }
}
