package com.easyagents.agent.runtime.event;

import com.easyagents.agent.runtime.AgentRuntimeException;
import com.easyagents.agent.runtime.AgentRuntimeExecutionContext;
import reactor.core.publisher.Sinks;

import java.util.Map;
import java.util.Optional;

/**
 * AgentScope 运行时的旁路事件桥。
 *
 * <p>该类只负责 Easy-Agents 自己的旁路监察事件，不负责 AgentScope 主线路事件。
 * 主线路事件来自 {@code agent.stream(...)}，会进入模型会话、工具结果和最终输出的正常顺序；
 * 旁路事件只给调用方观察运行过程，例如知识库检索、自动上下文压缩、工具审批和 Skill 步骤。</p>
 *
 * <p>旁路事件不会写入 AgentScope memory/session，也不会修改 AgentScope {@code Msg}。仅作观察与展示使用</p>
 */
public class AgentRuntimeEventBridge {

    private final AgentRuntimeExecutionContext fallbackContext;
    private final AgentRuntimeTurnContextHolder turnContextHolder;

    /**
     * 创建旁路事件桥。
     *
     * @param fallbackContext 运行时级上下文，当前轮次未设置时作为身份信息来源
     * @param turnContextHolder 当前运行轮次上下文持有器
     */
    public AgentRuntimeEventBridge(AgentRuntimeExecutionContext fallbackContext,
                                   AgentRuntimeTurnContextHolder turnContextHolder) {
        this.fallbackContext = fallbackContext;
        this.turnContextHolder = turnContextHolder;
    }

    /**
     * 创建固定 sink 的旁路事件桥，主要用于旧测试和旧辅助构造路径。
     *
     * @param fallbackContext 运行时级上下文
     * @param eventSink 旁路事件 sink
     * @return 旁路事件桥
     */
    public static AgentRuntimeEventBridge fixed(AgentRuntimeExecutionContext fallbackContext,
                                                Sinks.Many<AgentRuntimeEvent> eventSink) {
        AgentRuntimeTurnContextHolder holder = new AgentRuntimeTurnContextHolder();
        AgentRuntimeEventBridge bridge = new AgentRuntimeEventBridge(fallbackContext, holder);
        holder.set(new AgentRuntimeTurnContext(null, eventSink, bridge));
        return bridge;
    }

    /**
     * 创建指定类型的旁路事件，并自动补齐公共运行身份信息。
     *
     * @param type 旁路事件类型
     * @return 已补齐公共字段的运行时事件
     */
    public AgentRuntimeEvent event(AgentRuntimeEventType type) {
        AgentRuntimeEvent event = AgentRuntimeEvent.of(type);
        enrich(event);
        return event;
    }

    /**
     * 发射一条旁路事件。
     *
     * <p>当前没有运行轮次或没有旁路 sink 时直接忽略。这允许 adapter 在非流式测试、
     * 初始化阶段或主线路未建立旁路订阅时保持无副作用。</p>
     *
     * @param event 旁路事件
     */
    public void emit(AgentRuntimeEvent event) {
        if (event == null) {
            return;
        }
        Optional<Sinks.Many<AgentRuntimeEvent>> sink = eventSink();
        if (sink.isEmpty()) {
            return;
        }
        enrich(event);
        Sinks.EmitResult result = sink.get().tryEmitNext(event);
        if (result.isFailure()) {
            throw new AgentRuntimeException("Failed to emit agent runtime side event: " + result);
        }
    }

    /**
     * 获取当前轮次合并后的执行上下文。
     *
     * @return 当前执行上下文
     */
    public AgentRuntimeExecutionContext executionContext() {
        return turnContextHolder == null ? fallbackContext : turnContextHolder.executionContext(fallbackContext);
    }

    /**
     * 补齐旁路事件的公共身份信息。
     *
     * @param event 旁路事件
     */
    public void enrich(AgentRuntimeEvent event) {
        if (event == null) {
            return;
        }
        AgentRuntimeExecutionContext context = executionContext();
        if (context == null) {
            return;
        }
        if (event.getTraceId() == null || event.getTraceId().isBlank()) {
            event.setTraceId(context.getTraceId());
        }
        if (event.getSessionId() == null || event.getSessionId().isBlank()) {
            event.setSessionId(context.getSessionId());
        }
        if ((event.getAgentId() == null || event.getAgentId().isBlank())
                && context.getAgentDefinition() != null) {
            event.setAgentId(context.getAgentDefinition().getAgentId());
        }
        if (context.getRequestId() != null && !context.getRequestId().isBlank()) {
            event.getMetadata().putIfAbsent("requestId", context.getRequestId());
        }
        Map<String, Object> metadata = context.getMetadata();
        if (metadata != null && !metadata.isEmpty()) {
            metadata.forEach(event.getMetadata()::putIfAbsent);
        }
    }

    private Optional<Sinks.Many<AgentRuntimeEvent>> eventSink() {
        return turnContextHolder == null ? Optional.empty() : turnContextHolder.eventSink();
    }
}
