package tech.easyflow.agent.runtime.asynctool;

import com.easyagents.agent.runtime.tool.asynctool.AsyncToolTaskStatus;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import tech.easyflow.agent.config.AgentRuntimeProperties;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.UnaryOperator;

/**
 * 基于 Redis 单 key 的 Agent 异步工具任务存储。
 */
@Service
public class RedisAgentAsyncToolTaskStore implements AgentAsyncToolTaskStore {

    private static final String KEY_PREFIX = "easyflow:agent:async-tool:";

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

    /**
     * 创建 Redis 任务存储。
     *
     * @param stringRedisTemplate Redis 字符串模板
     * @param objectMapper JSON mapper
     * @param properties Agent runtime 配置
     */
    public RedisAgentAsyncToolTaskStore(StringRedisTemplate stringRedisTemplate,
                                        ObjectMapper objectMapper,
                                        AgentRuntimeProperties properties) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.objectMapper = objectMapper;
        this.properties = properties;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void create(AgentAsyncToolTaskRecord record) {
        if (record == null) {
            throw new BusinessException("异步工具任务不能为空");
        }
        String sessionId = requireText(record.getSessionId(), "异步工具任务 sessionId 不能为空");
        String taskId = requireText(record.getTaskId(), "异步工具任务 taskId 不能为空");
        record.setSessionScopedKey(key(sessionId, taskId));
        Duration ttl = taskTtl();
        record.setTtlSeconds(ttl.toSeconds());
        write(record, ttl);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Optional<AgentAsyncToolTaskRecord> get(String sessionId, String taskId) {
        String value = stringRedisTemplate.opsForValue().get(key(sessionId, taskId));
        if (!StringUtils.hasText(value)) {
            return Optional.empty();
        }
        return Optional.of(read(value));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Optional<AgentAsyncToolTaskRecord> update(String sessionId,
                                                     String taskId,
                                                     UnaryOperator<AgentAsyncToolTaskRecord> updater) {
        Optional<AgentAsyncToolTaskRecord> existing = get(sessionId, taskId);
        if (existing.isEmpty()) {
            return Optional.empty();
        }
        AgentAsyncToolTaskRecord updated = updater == null ? existing.get() : updater.apply(existing.get());
        if (updated == null) {
            return Optional.empty();
        }
        updated.setUpdatedAt(Instant.now());
        write(updated, remainingTtl(sessionId, taskId));
        return Optional.of(updated);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<AgentAsyncToolTaskRecord> list(String sessionId, AsyncToolTaskStatus status) {
        String safeSessionId = requireText(sessionId, "异步工具任务 sessionId 不能为空");
        List<AgentAsyncToolTaskRecord> result = new ArrayList<>();
        ScanOptions options = ScanOptions.scanOptions().match(KEY_PREFIX + safeSessionId + ":*").count(100).build();
        try (Cursor<String> cursor = stringRedisTemplate.scan(options)) {
            while (cursor.hasNext()) {
                String key = cursor.next();
                String value = stringRedisTemplate.opsForValue().get(key);
                if (!StringUtils.hasText(value)) {
                    continue;
                }
                AgentAsyncToolTaskRecord record = read(value);
                if (status == null || status == record.getStatus()) {
                    result.add(record);
                }
            }
        }
        result.sort(Comparator.comparing(AgentAsyncToolTaskRecord::getCreatedAt,
                Comparator.nullsLast(Comparator.reverseOrder())));
        return result;
    }

    private void write(AgentAsyncToolTaskRecord record, Duration ttl) {
        try {
            stringRedisTemplate.opsForValue().set(record.getSessionScopedKey(),
                    objectMapper.writeValueAsString(record), Math.max(1L, ttl.toSeconds()), TimeUnit.SECONDS);
        } catch (Exception e) {
            throw new BusinessException("写入异步工具任务状态失败：" + safeMessage(e));
        }
    }

    private AgentAsyncToolTaskRecord read(String value) {
        try {
            return objectMapper.readValue(value, AgentAsyncToolTaskRecord.class);
        } catch (Exception e) {
            throw new BusinessException("读取异步工具任务状态失败：" + safeMessage(e));
        }
    }

    private Duration remainingTtl(String sessionId, String taskId) {
        Long seconds = stringRedisTemplate.getExpire(key(sessionId, taskId), TimeUnit.SECONDS);
        if (seconds == null || seconds <= 0L) {
            return taskTtl();
        }
        return Duration.ofSeconds(seconds);
    }

    private Duration taskTtl() {
        Duration ttl = properties == null ? Duration.ofHours(24) : properties.getAsyncToolTaskTtl();
        return ttl == null || ttl.isZero() || ttl.isNegative() ? Duration.ofHours(24) : ttl;
    }

    private String key(String sessionId, String taskId) {
        return KEY_PREFIX
                + requireText(sessionId, "异步工具任务 sessionId 不能为空")
                + ":"
                + requireText(taskId, "异步工具任务 taskId 不能为空");
    }

    private String requireText(String value, String message) {
        if (!StringUtils.hasText(value)) {
            throw new BusinessException(message);
        }
        return value.trim();
    }

    private String safeMessage(Exception e) {
        return e == null || e.getMessage() == null || e.getMessage().isBlank()
                ? "未知错误"
                : e.getMessage();
    }
}
