package tech.easyflow.ai.easyagentsflow.repository;

import com.easyagents.flow.core.chain.ChainDefinition;
import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.ai.easyagentsflow.config.WorkflowRuntimeProperties;
import tech.easyflow.ai.easyagentsflow.event.WorkflowDefinitionChangedEvent;
import tech.easyflow.ai.easyagentsflow.support.PublishedWorkflowDefinitionIds;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * {@link WorkflowDefinitionCache} 命中、失效和编译去重回归测试。
 */
public class WorkflowDefinitionCacheTest {

    /**
     * 验证同一版本重复执行只编译一次。
     */
    @Test
    public void shouldCompileOnlyOnceForRepeatedReads() {
        InMemoryVersionStore versionStore = new InMemoryVersionStore();
        WorkflowDefinitionCache cache = cache(versionStore);
        AtomicInteger loads = new AtomicInteger();

        ChainDefinition first = cache.get("1", () -> definition("1", loads));
        ChainDefinition second = cache.get("1", () -> definition("1", loads));

        Assert.assertSame(first, second);
        Assert.assertEquals(1, loads.get());
    }

    /**
     * 验证工作流变更后草稿态和发布态缓存同时失效。
     */
    @Test
    public void shouldInvalidateDraftAndPublishedDefinitionsTogether() {
        InMemoryVersionStore versionStore = new InMemoryVersionStore();
        WorkflowDefinitionCache cache = cache(versionStore);
        AtomicInteger loads = new AtomicInteger();
        String publishedId = PublishedWorkflowDefinitionIds.published("2");

        ChainDefinition draftBefore = cache.get("2", () -> definition("2", loads));
        ChainDefinition publishedBefore = cache.get(publishedId, () -> definition(publishedId, loads));
        cache.onDefinitionChanged(new WorkflowDefinitionChangedEvent("2"));
        ChainDefinition draftAfter = cache.get("2", () -> definition("2", loads));
        ChainDefinition publishedAfter = cache.get(publishedId, () -> definition(publishedId, loads));

        Assert.assertNotSame(draftBefore, draftAfter);
        Assert.assertNotSame(publishedBefore, publishedAfter);
        Assert.assertEquals(4, loads.get());
    }

    /**
     * 创建测试缓存。
     *
     * @param versionStore 版本令牌仓储
     * @return 定义缓存
     */
    private WorkflowDefinitionCache cache(WorkflowDefinitionVersionStore versionStore) {
        WorkflowRuntimeProperties properties = new WorkflowRuntimeProperties();
        properties.setDefinitionCacheMaxEntries(4);
        return new WorkflowDefinitionCache(versionStore, properties);
    }

    /**
     * 创建测试定义并记录编译次数。
     *
     * @param id 定义 ID
     * @param loads 编译计数
     * @return 工作流定义
     */
    private ChainDefinition definition(String id, AtomicInteger loads) {
        loads.incrementAndGet();
        ChainDefinition definition = new ChainDefinition();
        definition.setId(id);
        return definition;
    }

    /**
     * 进程内版本令牌测试仓储。
     */
    private static final class InMemoryVersionStore implements WorkflowDefinitionVersionStore {

        private final Map<String, String> tokens = new ConcurrentHashMap<>();

        /**
         * {@inheritDoc}
         */
        @Override
        public String currentToken(String definitionId) {
            return tokens.computeIfAbsent(definitionId, ignored -> UUID.randomUUID().toString());
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void invalidateWorkflow(String workflowId) {
            tokens.put(workflowId, UUID.randomUUID().toString());
            tokens.put(PublishedWorkflowDefinitionIds.published(workflowId), UUID.randomUUID().toString());
        }
    }
}
