package tech.easyflow.agent.runtime.asynctool;

import com.easyagents.agent.runtime.tool.AgentToolContext;
import com.easyagents.agent.runtime.tool.asynctool.*;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.StringUtils;
import tech.easyflow.agent.runtime.tool.AgentToolExecutionResult;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.time.Instant;
import java.util.*;

/**
 * EasyFlow Agent 异步业务工具基类。
 */
public abstract class AbstractAgentAsyncSubTools implements AsyncSubTools {

    private static final String ERROR_TYPE_NOT_FOUND = "TASK_NOT_FOUND";
    private static final String ERROR_TYPE_EXCEPTION = "EXCEPTION";

    private final AgentAsyncToolTaskStore taskStore;
    private final ThreadPoolTaskExecutor taskExecutor;

    /**
     * 创建异步业务工具基类。
     *
     * @param taskStore 任务存储
     * @param taskExecutor 后台执行器
     */
    protected AbstractAgentAsyncSubTools(AgentAsyncToolTaskStore taskStore,
                                         ThreadPoolTaskExecutor taskExecutor) {
        this.taskStore = taskStore;
        this.taskExecutor = taskExecutor;
    }

    /**
     * 获取工具类型。
     *
     * @return 工具类型
     */
    protected abstract String toolType();

    /**
     * 获取运行时工具名。
     *
     * @return 运行时工具名
     */
    protected abstract String toolName();

    /**
     * 获取用户可见工具名称。
     *
     * @return 用户可见工具名称
     */
    protected abstract String displayName();

    /**
     * 获取业务资源 ID。
     *
     * @return 业务资源 ID
     */
    protected abstract String businessId();

    /**
     * 执行业务工具。
     *
     * @param arguments 调用参数
     * @return 执行结果
     */
    protected abstract AgentToolExecutionResult executeBusiness(Map<String, Object> arguments);

    /**
     * {@inheritDoc}
     */
    @Override
    public AsyncToolSubmitResult submit(Map<String, Object> arguments, AgentToolContext context) {
        String sessionId = requireSessionId(context);
        String taskId = newTaskId();
        AgentAsyncToolTaskRecord record = new AgentAsyncToolTaskRecord();
        record.setTaskId(taskId);
        record.setToolType(toolType());
        record.setToolName(toolName());
        record.setBusinessId(businessId());
        record.setStatus(AsyncToolTaskStatus.PENDING);
        record.setArguments(arguments == null ? Map.of() : new LinkedHashMap<>(arguments));
        record.setSummary(displayName() + "任务已提交");
        record.setRequestId(context == null ? null : context.getRequestId());
        record.setTraceId(context == null ? null : context.getTraceId());
        record.setSessionId(sessionId);
        record.setAgentId(context == null ? null : context.getAgentId());
        record.setToolCallId(context == null ? null : context.getToolCallId());
        record.getMetadata().put("toolDisplayName", displayName());
        appendEvent(record, "SUBMITTED", displayName() + "任务已提交");
        taskStore.create(record);
        dispatch(sessionId, record.getTaskId(), record.getArguments());

        AsyncToolSubmitResult result = new AsyncToolSubmitResult();
        result.setTaskId(taskId);
        result.setStatus(AsyncToolTaskStatus.PENDING);
        result.setCursor(0L);
        result.setSummary(record.getSummary());
        result.setNextAction(toolName() + "_observe 查看任务进度。");
        result.getMetadata().put("toolDisplayName", displayName());
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AsyncToolTaskView observe(AsyncToolObserveRequest request, AgentToolContext context) {
        return taskView(request == null ? null : request.getTaskId(),
                request == null ? null : request.getCursor(),
                request == null ? null : request.getLimit(),
                context);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AsyncToolTaskView result(AsyncToolResultRequest request, AgentToolContext context) {
        return taskView(request == null ? null : request.getTaskId(),
                request == null ? null : request.getCursor(),
                request == null ? null : request.getLimit(),
                context);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AsyncToolCancelResult cancel(AsyncToolCancelRequest request, AgentToolContext context) {
        AsyncToolCancelResult result = new AsyncToolCancelResult();
        result.setTaskId(request == null ? null : request.getTaskId());
        result.setStatus(AsyncToolTaskStatus.FAILED);
        result.setErrorMessage("当前异步工具不支持取消正在执行的任务");
        result.setMessage("不支持取消");
        result.getMetadata().put("toolDisplayName", displayName());
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public AsyncToolTaskListResult list(AsyncToolListRequest request, AgentToolContext context) {
        String sessionId = requireSessionId(context);
        AsyncToolTaskStatus status = request == null ? null : request.getStatus();
        List<AsyncToolTaskSummary> tasks = new ArrayList<>();
        for (AgentAsyncToolTaskRecord record : taskStore.list(sessionId, status)) {
            tasks.add(summary(record));
        }
        AsyncToolTaskListResult result = new AsyncToolTaskListResult();
        result.setTasks(tasks);
        result.getMetadata().put("toolDisplayName", displayName());
        return result;
    }

    private void dispatch(String sessionId, String taskId, Map<String, Object> arguments) {
        try {
            taskExecutor.execute(() -> executeTask(sessionId, taskId, arguments));
        } catch (Exception e) {
            taskStore.update(sessionId, taskId, record -> fail(record, e));
            throw new BusinessException("提交异步工具任务失败：" + safeMessage(e));
        }
    }

    private void executeTask(String sessionId, String taskId, Map<String, Object> arguments) {
        try {
            taskStore.update(sessionId, taskId, record -> {
                record.setStatus(AsyncToolTaskStatus.RUNNING);
                record.setSummary(displayName() + "任务执行中");
                appendEvent(record, "RUNNING", displayName() + "任务执行中");
                return record;
            });
            AgentToolExecutionResult executionResult = executeBusiness(arguments);
            taskStore.update(sessionId, taskId, record -> {
                record.setStatus(AsyncToolTaskStatus.SUCCEEDED);
                record.setSummary(displayName() + "任务已完成");
                record.setResult(executionResult == null ? null : executionResult.getResult());
                record.setBusinessExecutionId(executionResult == null ? null : executionResult.getBusinessExecutionId());
                appendEvent(record, "SUCCEEDED", displayName() + "任务已完成");
                return record;
            });
        } catch (Exception e) {
            taskStore.update(sessionId, taskId, record -> fail(record, e));
        }
    }

    private AgentAsyncToolTaskRecord fail(AgentAsyncToolTaskRecord record, Exception error) {
        record.setStatus(AsyncToolTaskStatus.FAILED);
        record.setSummary(displayName() + "任务执行失败");
        record.setErrorType(ERROR_TYPE_EXCEPTION);
        record.setErrorMessage(safeMessage(error));
        appendEvent(record, "FAILED", record.getErrorMessage());
        return record;
    }

    private AsyncToolTaskView taskView(String taskId, Long cursor, Integer limit, AgentToolContext context) {
        String sessionId = requireSessionId(context);
        if (!StringUtils.hasText(taskId)) {
            return notFoundView(taskId, cursor, "任务 ID 不能为空");
        }
        return taskStore.get(sessionId, taskId)
                .map(record -> toView(record, cursor, limit))
                .orElseGet(() -> notFoundView(taskId, cursor, "异步工具任务不存在或已过期"));
    }

    private AsyncToolTaskView toView(AgentAsyncToolTaskRecord record, Long cursor, Integer limit) {
        long safeCursor = cursor == null ? 0L : Math.max(0L, cursor);
        int safeLimit = limit == null || limit <= 0 ? 20 : Math.min(limit, 100);
        List<AsyncToolTaskEvent> events = new ArrayList<>();
        for (AsyncToolTaskEvent event : record.getEvents()) {
            if (event.getSequence() != null && event.getSequence() > safeCursor) {
                events.add(event);
            }
            if (events.size() >= safeLimit) {
                break;
            }
        }
        Long nextCursor = events.isEmpty()
                ? safeCursor
                : events.get(events.size() - 1).getSequence();
        AsyncToolTaskView view = new AsyncToolTaskView();
        view.setTaskId(record.getTaskId());
        view.setStatus(record.getStatus());
        view.setCursor(safeCursor);
        view.setNextCursor(nextCursor);
        view.setSummary(record.getSummary());
        view.setNextAction(nextAction(record.getStatus()));
        view.setEvents(events);
        view.setResult(record.getResult());
        view.setErrorMessage(record.getErrorMessage());
        view.setErrorType(record.getErrorType());
        view.setTerminal(record.getStatus() != null && record.getStatus().isTerminal());
        view.setResultAvailable(record.getStatus() == AsyncToolTaskStatus.SUCCEEDED && record.getResult() != null);
        view.getMetadata().put("toolDisplayName", displayName());
        putIfNotNull(view.getPayload(), "businessId", record.getBusinessId());
        putIfNotNull(view.getPayload(), "businessExecutionId", record.getBusinessExecutionId());
        return view;
    }

    private AsyncToolTaskView notFoundView(String taskId, Long cursor, String message) {
        AsyncToolTaskView view = new AsyncToolTaskView();
        view.setTaskId(taskId);
        view.setStatus(AsyncToolTaskStatus.FAILED);
        view.setCursor(cursor == null ? 0L : cursor);
        view.setNextCursor(cursor == null ? 0L : cursor);
        view.setSummary(message);
        view.setErrorType(ERROR_TYPE_NOT_FOUND);
        view.setErrorMessage(message);
        view.setTerminal(true);
        view.setResultAvailable(false);
        view.getMetadata().put("toolDisplayName", displayName());
        return view;
    }

    private AsyncToolTaskSummary summary(AgentAsyncToolTaskRecord record) {
        AsyncToolTaskSummary summary = new AsyncToolTaskSummary();
        summary.setTaskId(record.getTaskId());
        summary.setStatus(record.getStatus());
        summary.setSummary(record.getSummary());
        summary.setCreatedAt(record.getCreatedAt());
        summary.setUpdatedAt(record.getUpdatedAt());
        summary.getPayload().put("toolName", record.getToolName());
        summary.getPayload().put("toolDisplayName", displayName());
        return summary;
    }

    private void appendEvent(AgentAsyncToolTaskRecord record, String type, String text) {
        AsyncToolTaskEvent event = new AsyncToolTaskEvent();
        event.setSequence((long) record.getEvents().size() + 1L);
        event.setType(type);
        event.setText(text);
        event.setCreatedAt(Instant.now());
        record.getEvents().add(event);
    }

    private String nextAction(AsyncToolTaskStatus status) {
        if (status != null && status.isTerminal()) {
            return "任务已结束。";
        }
        return toolName() + "_observe 继续查看任务进度。";
    }

    private String requireSessionId(AgentToolContext context) {
        if (context == null || !StringUtils.hasText(context.getSessionId())) {
            throw new BusinessException("异步工具任务缺少 Agent session 上下文");
        }
        return context.getSessionId();
    }

    private String newTaskId() {
        String idPart = UUID.randomUUID().toString().replace("-", "");
        return "async_" + idPart;
    }

    private void putIfNotNull(Map<String, Object> target, String key, Object value) {
        if (value != null) {
            target.put(key, value);
        }
    }

    private String safeMessage(Exception e) {
        return e == null || e.getMessage() == null || e.getMessage().isBlank()
                ? "异步工具任务执行失败"
                : e.getMessage();
    }
}
