package com.easyagents.agent.runtime.event;

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

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * AgentScope 运行时的单轮执行上下文。
 */
public class AgentRuntimeTurnContext {

    /**
     * 本轮运行上下文。
     */
    private final AgentRuntimeExecutionContext executionContext;

    /**
     * 本轮旁路事件 sink。
     *
     * <p>该字段只承载旁线路监察事件，不承载 AgentScope 主线路 stream 事件。</p>
     */
    private final Sinks.Many<AgentRuntimeEvent> eventSink;

    /**
     * 本轮旁路事件桥。
     *
     * <p>adapter 和 observer 应优先通过 bridge 发射旁路事件，避免各处重复拼接
     * trace/session/request 等公共字段。</p>
     */
    private final AgentRuntimeEventBridge eventBridge;

    /**
     * 创建单轮执行上下文。
     *
     * @param executionContext 本轮运行上下文
     * @param eventSink 本轮旁路事件 sink
     */
    public AgentRuntimeTurnContext(AgentRuntimeExecutionContext executionContext,
                                   Sinks.Many<AgentRuntimeEvent> eventSink) {
        this(executionContext, eventSink, null);
    }

    /**
     * 创建单轮执行上下文。
     *
     * @param executionContext 本轮运行上下文
     * @param eventSink 本轮旁路事件 sink
     * @param eventBridge 本轮旁路事件桥
     */
    public AgentRuntimeTurnContext(AgentRuntimeExecutionContext executionContext,
                                   Sinks.Many<AgentRuntimeEvent> eventSink,
                                   AgentRuntimeEventBridge eventBridge) {
        this.executionContext = executionContext;
        this.eventSink = eventSink;
        this.eventBridge = eventBridge;
    }

    /**
     * 获取本轮运行上下文。
     *
     * @return 本轮运行上下文
     */
    public AgentRuntimeExecutionContext getExecutionContext() {
        return executionContext;
    }

    /**
     * 获取本轮旁路事件 sink。
     *
     * @return 本轮旁路事件 sink
     */
    public Sinks.Many<AgentRuntimeEvent> getEventSink() {
        return eventSink;
    }

    /**
     * 获取本轮旁路事件桥。
     *
     * @return 本轮旁路事件桥
     */
    public AgentRuntimeEventBridge getEventBridge() {
        return eventBridge;
    }

    /**
     * 构建以当前轮信息覆盖运行时信息后的上下文。
     *
     * @param fallback 运行时级上下文
     * @return 当前轮上下文，未设置时返回运行时级上下文
     */
    public AgentRuntimeExecutionContext mergeWith(AgentRuntimeExecutionContext fallback) {
        if (executionContext == null) {
            return fallback;
        }
        AgentRuntimeExecutionContext merged = new AgentRuntimeExecutionContext();
        merged.setRequestId(firstNonBlank(executionContext.getRequestId(), fallback == null ? null : fallback.getRequestId()));
        merged.setTraceId(firstNonBlank(executionContext.getTraceId(), fallback == null ? null : fallback.getTraceId()));
        merged.setSessionId(firstNonBlank(executionContext.getSessionId(), fallback == null ? null : fallback.getSessionId()));
        merged.setAgentDefinition(executionContext.getAgentDefinition() == null && fallback != null
                ? fallback.getAgentDefinition()
                : executionContext.getAgentDefinition());
        merged.setRuntimeContext(executionContext.getRuntimeContext() == null && fallback != null
                ? fallback.getRuntimeContext()
                : executionContext.getRuntimeContext());
        merged.setUserMessage(executionContext.getUserMessage());
        merged.setMemorySnapshot(executionContext.getMemorySnapshot());
        merged.setToolInvokers(fallback == null ? executionContext.getToolInvokers() : fallback.getToolInvokers());
        merged.setKnowledgeRegistrations(fallback == null
                ? executionContext.getKnowledgeRegistrations()
                : fallback.getKnowledgeRegistrations());
        merged.setSessionStore(fallback == null ? executionContext.getSessionStore() : fallback.getSessionStore());
        merged.setConversationRecorder(fallback == null
                ? executionContext.getConversationRecorder()
                : fallback.getConversationRecorder());
        merged.setMetadata(mergedMetadata(fallback, executionContext));
        merged.setCancelReason(executionContext.getCancelReason());
        return merged;
    }

    private Map<String, Object> mergedMetadata(AgentRuntimeExecutionContext fallback,
                                               AgentRuntimeExecutionContext current) {
        Map<String, Object> metadata = new LinkedHashMap<>();
        if (fallback != null && fallback.getMetadata() != null) {
            metadata.putAll(fallback.getMetadata());
        }
        if (current != null && current.getMetadata() != null) {
            metadata.putAll(current.getMetadata());
        }
        return metadata;
    }

    private String firstNonBlank(String first, String second) {
        return first == null || first.isBlank() ? second : first;
    }
}
