package com.easyagents.agent.runtime.prompt;

import com.easyagents.agent.runtime.AgentDefinition;
import com.easyagents.agent.runtime.tool.AgentToolSpec;

import java.util.ArrayList;
import java.util.List;

/**
 * 组合智能体运行时使用的系统提示词。
 */
public final class SystemPromptComposer {

    private static final String KNOWLEDGE_TOOL_PROTOCOL = """
            Knowledge tool protocol:
            - Knowledge tools provide grounded information for the scopes described in their tool descriptions.
            - Before answering a request involving facts, policies, procedures, entitlements, prices, product or service details, or other information within a knowledge tool's scope, call the most relevant knowledge tool first.
            - Do not rely only on general model knowledge for claims that fall within an available knowledge tool's scope.
            - If it is uncertain whether the request falls within a knowledge tool's scope, prefer making one retrieval call.
            - Build a concise, standalone query from the current request and only the necessary conversation context. Resolve pronouns and omitted subjects, and preserve relevant names, time, location, product, membership level, constraints, and user intent. Do not copy the entire conversation.
            - If the returned results are empty or do not address the request, reformulate the query and retry once when a materially different query is possible. Do not repeat the same query or retrieve indefinitely.
            - Base knowledge-backed claims only on information actually returned by the tools. Never imply that a knowledge base contains information that was not returned.
            - If retrieval remains insufficient, follow the Agent's configured system prompt for the response strategy.
            - Skip retrieval for greetings, casual conversation, pure writing or translation, and tasks that clearly do not depend on knowledge-base facts.
            """.strip();

    private static final String ASYNC_TOOL_PROTOCOL = """
            Async tool protocol:
            - Async tools may expose submit, observe, result, cancel, and list sub-tools. Treat these sub-tools as one user-facing tool.
            - Do not ask the user to choose submit, observe, result, cancel, or list. These are internal execution phases.
            - For a normal user request to use an async tool, call its submit sub-tool first with the user-provided arguments by default.
            - After submit returns task_id, immediately call observe with that task_id to check progress.
            - If the task is completed and result is available, use the returned result to answer the user.
            - If the task is still running after observation, tell the user that the task is running and keep task_id/next_action for later tool calls.
            - Use result, list, or cancel directly only when the user explicitly asks to get a known task result, list tasks, or cancel a task.
            """.strip();

    /**
     * 阻止工具类被实例化。
     */
    private SystemPromptComposer() {
    }

    /**
     * 按固定顺序组合用户提示词与运行时工具协议。
     *
     * @param definition 智能体定义
     * @return 最终系统提示词；没有任何提示词时返回原始空值
     */
    public static String compose(AgentDefinition definition) {
        if (definition == null) {
            return null;
        }
        boolean knowledgeProtocolEnabled = hasEnabledKnowledgeTool(definition);
        boolean asyncProtocolEnabled = hasAsyncTool(definition);
        String userPrompt = definition.getSystemPrompt();
        if (!knowledgeProtocolEnabled && !asyncProtocolEnabled) {
            return userPrompt;
        }

        List<String> promptSections = new ArrayList<>(3);
        if (userPrompt != null && !userPrompt.isBlank()) {
            promptSections.add(userPrompt.stripTrailing());
        }
        if (knowledgeProtocolEnabled) {
            promptSections.add(KNOWLEDGE_TOOL_PROTOCOL);
        }
        if (asyncProtocolEnabled) {
            promptSections.add(ASYNC_TOOL_PROTOCOL);
        }
        return String.join("\n\n", promptSections);
    }

    /**
     * 判断当前定义是否会注册可调用的知识库工具。
     *
     * @param definition 智能体定义
     * @return 知识库工具可用时返回 true
     */
    private static boolean hasEnabledKnowledgeTool(AgentDefinition definition) {
        return definition.getExecutionOptions() != null
                && definition.getExecutionOptions().isToolCallingEnabled()
                && definition.getKnowledgeSpecs() != null
                && !definition.getKnowledgeSpecs().isEmpty();
    }

    /**
     * 判断当前定义是否包含异步工具。
     *
     * @param definition 智能体定义
     * @return 包含异步工具时返回 true
     */
    private static boolean hasAsyncTool(AgentDefinition definition) {
        if (definition.getToolSpecs() == null) {
            return false;
        }
        for (AgentToolSpec toolSpec : definition.getToolSpecs()) {
            // AsyncToolSpecExpander 会为生成的全部子工具写入该运行时元数据。
            if (toolSpec != null && Boolean.TRUE.equals(toolSpec.getMetadata().get("asyncTool"))) {
                return true;
            }
        }
        return false;
    }
}
