package tech.easyflow.agent.distributed;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import tech.easyflow.agent.config.AgentRuntimeProperties;
import tech.easyflow.agent.distributed.AgentRuntimeCommandResult;
import tech.easyflow.agent.distributed.AgentRuntimeCommandResultRegistry;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.time.Duration;

/**
 * {@link AgentRuntimeCommandResultRegistry} 回归测试。
 */
public class AgentRuntimeCommandResultRegistryTest {

    /**
     * 验证成功结果可被等待方读取。
     */
    @Test
    public void waitForResultShouldReturnSuccessResult() {
        StringRedisTemplate redisTemplate = Mockito.mock(StringRedisTemplate.class);
        @SuppressWarnings("unchecked")
        ValueOperations<String, String> valueOperations = Mockito.mock(ValueOperations.class);
        Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        Mockito.when(valueOperations.get("easyflow:agent:runtime:command-result:cmd-1"))
                .thenReturn("{\"success\":true,\"message\":\"OK\"}");
        AgentRuntimeCommandResultRegistry registry = registry(redisTemplate);

        AgentRuntimeCommandResult result = registry.waitForResult("cmd-1");

        Assert.assertTrue(result.isSuccess());
        Assert.assertEquals("OK", result.getMessage());
    }

    /**
     * 验证写入失败结果时使用配置的 TTL。
     */
    @Test
    public void markFailureShouldWriteResultWithTtl() {
        StringRedisTemplate redisTemplate = Mockito.mock(StringRedisTemplate.class);
        @SuppressWarnings("unchecked")
        ValueOperations<String, String> valueOperations = Mockito.mock(ValueOperations.class);
        Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        AgentRuntimeProperties properties = properties();
        AgentRuntimeCommandResultRegistry registry =
                new AgentRuntimeCommandResultRegistry(redisTemplate, new ObjectMapper(), properties);

        registry.markFailure("cmd-1", "failed");

        Mockito.verify(valueOperations).set(
                ArgumentMatchers.eq("easyflow:agent:runtime:command-result:cmd-1"),
                ArgumentMatchers.contains("\"success\":false"),
                ArgumentMatchers.eq(properties.getCommandResultTtl()));
    }

    /**
     * 验证等待超时时抛出明确业务异常。
     */
    @Test
    public void waitForResultShouldThrowBusinessExceptionWhenTimeout() {
        StringRedisTemplate redisTemplate = Mockito.mock(StringRedisTemplate.class);
        @SuppressWarnings("unchecked")
        ValueOperations<String, String> valueOperations = Mockito.mock(ValueOperations.class);
        Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations);
        Mockito.when(valueOperations.get(ArgumentMatchers.anyString())).thenReturn(null);
        AgentRuntimeCommandResultRegistry registry = registry(redisTemplate);

        BusinessException exception = Assert.assertThrows(
                BusinessException.class,
                () -> registry.waitForResult("cmd-1"));

        Assert.assertEquals("Agent 运行节点响应超时，请稍后重试", exception.getMessage());
    }

    private AgentRuntimeCommandResultRegistry registry(StringRedisTemplate redisTemplate) {
        return new AgentRuntimeCommandResultRegistry(redisTemplate, new ObjectMapper(), properties());
    }

    private AgentRuntimeProperties properties() {
        AgentRuntimeProperties properties = new AgentRuntimeProperties();
        properties.setCommandResultTimeout(Duration.ofMillis(10));
        properties.setCommandResultTtl(Duration.ofMinutes(5));
        return properties;
    }
}
