package tech.easyflow.agent.distributed;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import tech.easyflow.agent.config.AgentRuntimeProperties;
import tech.easyflow.common.web.exceptions.BusinessException;

/**
 * Agent 运行态远程命令结果注册表。
 */
@Component
public class AgentRuntimeCommandResultRegistry {

    private static final String RESULT_PREFIX = "easyflow:agent:runtime:command-result:";
    private static final long POLL_INTERVAL_MILLIS = 50L;

    private final StringRedisTemplate stringRedisTemplate;
    private final ObjectMapper objectMapper;
    private final AgentRuntimeProperties properties;

    /**
     * 创建 Agent 运行态远程命令结果注册表。
     *
     * @param stringRedisTemplate Redis 字符串模板
     * @param objectMapper JSON 序列化器
     * @param properties Agent 运行配置
     */
    public AgentRuntimeCommandResultRegistry(StringRedisTemplate stringRedisTemplate,
                                             ObjectMapper objectMapper,
                                             AgentRuntimeProperties properties) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.objectMapper = objectMapper;
        this.properties = properties;
    }

    /**
     * 写入成功结果。
     *
     * @param commandId 命令 ID
     */
    public void markSuccess(String commandId) {
        AgentRuntimeCommandResult result = new AgentRuntimeCommandResult();
        result.setSuccess(true);
        result.setMessage("OK");
        writeResult(commandId, result);
    }

    /**
     * 写入失败结果。
     *
     * @param commandId 命令 ID
     * @param message 失败消息
     */
    public void markFailure(String commandId, String message) {
        AgentRuntimeCommandResult result = new AgentRuntimeCommandResult();
        result.setSuccess(false);
        result.setMessage(message == null || message.isBlank() ? "Agent 运行节点不可用，请重新发起对话" : message);
        writeResult(commandId, result);
    }

    /**
     * 等待远程命令结果。
     *
     * @param commandId 命令 ID
     * @return 命令结果
     */
    public AgentRuntimeCommandResult waitForResult(String commandId) {
        long deadline = System.nanoTime() + properties.getCommandResultTimeout().toNanos();
        while (System.nanoTime() <= deadline) {
            AgentRuntimeCommandResult result = readResult(commandId);
            if (result != null) {
                return result;
            }
            sleep();
        }
        throw new BusinessException("Agent 运行节点响应超时，请稍后重试");
    }

    /**
     * 删除远程命令结果。
     *
     * @param commandId 命令 ID
     */
    public void deleteResult(String commandId) {
        if (commandId == null || commandId.isBlank()) {
            return;
        }
        stringRedisTemplate.delete(resultKey(commandId));
    }

    private AgentRuntimeCommandResult readResult(String commandId) {
        if (commandId == null || commandId.isBlank()) {
            return null;
        }
        String value = stringRedisTemplate.opsForValue().get(resultKey(commandId));
        if (value == null || value.isBlank()) {
            return null;
        }
        try {
            return objectMapper.readValue(value, AgentRuntimeCommandResult.class);
        } catch (JsonProcessingException e) {
            throw new BusinessException("Agent 运行命令结果解析失败");
        }
    }

    private void writeResult(String commandId, AgentRuntimeCommandResult result) {
        if (commandId == null || commandId.isBlank()) {
            return;
        }
        try {
            stringRedisTemplate.opsForValue().set(
                    resultKey(commandId),
                    objectMapper.writeValueAsString(result),
                    properties.getCommandResultTtl());
        } catch (JsonProcessingException e) {
            throw new IllegalStateException("Agent 运行命令结果序列化失败", e);
        }
    }

    private String resultKey(String commandId) {
        return RESULT_PREFIX + commandId;
    }

    private void sleep() {
        try {
            Thread.sleep(POLL_INTERVAL_MILLIS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new BusinessException("Agent 运行节点响应等待被中断");
        }
    }
}
