package com.easyagents.agent.runtime.skill;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Skill 运行时上下文。
 */
public class AgentSkillRuntimeContext {

    /**
     * AgentScope SkillBox 内置的 Skill 加载工具名称。
     */
    public static final String LOAD_SKILL_TOOL_NAME = "load_skill_through_path";

    private final Map<String, AgentSkillBinding> skillBindings;
    private final Map<String, AgentSkillBinding> toolBindings;
    private final Map<String, AgentSkillLoadCall> pendingLoadCalls = new ConcurrentHashMap<>();
    private final Map<String, Boolean> emittedLoadCalls = new ConcurrentHashMap<>();
    private final Map<String, Boolean> activeSkills = new ConcurrentHashMap<>();

    private AgentSkillRuntimeContext(Map<String, AgentSkillBinding> skillBindings,
                                     Map<String, AgentSkillBinding> toolBindings) {
        this.skillBindings = skillBindings;
        this.toolBindings = toolBindings;
    }

    /**
     * 从 SkillBox 配置创建运行时上下文。
     *
     * @param spec SkillBox 配置
     * @return Skill 运行时上下文
     */
    public static AgentSkillRuntimeContext from(AgentSkillBoxSpec spec) {
        if (spec == null) {
            return new AgentSkillRuntimeContext(new LinkedHashMap<>(), new LinkedHashMap<>());
        }
        Map<String, AgentSkillBinding> skillBindings = new LinkedHashMap<>();
        for (AgentSkillSpec skillSpec : spec.getSkills()) {
            if (skillSpec.getSkillId() == null || skillSpec.getSkillId().isBlank()) {
                continue;
            }
            skillBindings.put(skillSpec.getSkillId(),
                    new AgentSkillBinding(skillSpec.getSkillId(), skillSpec.getName(),
                            displayName(skillSpec), spec.getSkillBoxId()));
        }
        Map<String, AgentSkillBinding> toolBindings = new LinkedHashMap<>();
        for (Map.Entry<String, List<String>> entry : spec.getToolBindings().entrySet()) {
            AgentSkillBinding binding = skillBindings.get(entry.getKey());
            if (binding == null || entry.getValue() == null) {
                continue;
            }
            for (String toolName : entry.getValue()) {
                if (toolName != null && !toolName.isBlank()) {
                    toolBindings.put(toolName, binding);
                }
            }
        }
        return new AgentSkillRuntimeContext(skillBindings, toolBindings);
    }

    /**
     * 判断是否为 Skill 加载工具。
     *
     * @param toolName 工具名称
     * @return 是 Skill 加载工具时为 true
     */
    public boolean isSkillLoadTool(String toolName) {
        return LOAD_SKILL_TOOL_NAME.equals(toolName);
    }

    /**
     * 根据 Skill ID 获取绑定关系。
     *
     * @param skillId Skill ID
     * @return 绑定关系
     */
    public AgentSkillBinding getSkillBinding(String skillId) {
        return skillBindings.get(skillId);
    }

    /**
     * 根据工具名称获取 Skill 绑定关系。
     *
     * @param toolName 工具名称
     * @return 绑定关系
     */
    public AgentSkillBinding getToolBinding(String toolName) {
        return toolBindings.get(toolName);
    }

    /**
     * 判断工具是否绑定到 Skill。
     *
     * @param toolName 工具名称
     * @return 绑定到 Skill 时为 true
     */
    public boolean isSkillBoundTool(String toolName) {
        return toolBindings.containsKey(toolName);
    }

    /**
     * 激活指定 Skill。
     *
     * @param skillId Skill ID
     */
    public void activateSkill(String skillId) {
        if (skillId != null && !skillId.isBlank()) {
            activeSkills.put(skillId, true);
        }
    }

    /**
     * 按 AgentScope 的真实 Skill 激活状态同步本地旁路上下文。
     *
     * <p>该状态只用于 Easy-Agents 判断旁路展示事件归属，不参与 AgentScope
     * memory/session，也不决定工具是否真的可被模型调用。</p>
     *
     * @param skillId Skill ID
     * @param active 是否激活
     */
    public void syncSkillActive(String skillId, boolean active) {
        if (skillId == null || skillId.isBlank()) {
            return;
        }
        if (active) {
            activeSkills.put(skillId, true);
            return;
        }
        activeSkills.remove(skillId);
    }

    /**
     * 判断 Skill 是否已激活。
     *
     * @param skillId Skill ID
     * @return 已激活时为 true
     */
    public boolean isSkillActive(String skillId) {
        return Boolean.TRUE.equals(activeSkills.get(skillId));
    }

    /**
     * 获取已激活 Skill 的工具绑定。
     *
     * @param toolName 工具名称
     * @return 已激活 Skill 的绑定关系
     */
    public AgentSkillBinding getActiveToolBinding(String toolName) {
        AgentSkillBinding binding = getToolBinding(toolName);
        if (binding == null || !isSkillActive(binding.getSkillId())) {
            return null;
        }
        return binding;
    }

    /**
     * 记录一次 Skill 加载工具调用。
     *
     * @param toolCallId 工具调用 ID
     * @param input 工具输入
     * @return Skill 加载调用
     */
    public AgentSkillLoadCall rememberLoadCall(String toolCallId, Map<String, Object> input) {
        if (toolCallId == null || input == null) {
            return null;
        }
        String skillId = stringValue(input.get("skillId"));
        String path = stringValue(input.get("path"));
        AgentSkillBinding binding = getSkillBinding(skillId);
        AgentSkillLoadCall call = new AgentSkillLoadCall(toolCallId, skillId,
                binding == null ? null : binding.getSkillName(),
                binding == null ? null : binding.getSkillDisplayName(),
                binding == null ? null : binding.getSkillBoxId(), path, input);
        pendingLoadCalls.put(toolCallId, call);
        return call;
    }

    /**
     * 判断 Skill 加载调用事件是否已经发射。
     *
     * @param toolCallId 工具调用 ID
     * @return 首次发射时为 true
     */
    public boolean markLoadCallEmitted(String toolCallId) {
        if (toolCallId == null) {
            return false;
        }
        return emittedLoadCalls.putIfAbsent(toolCallId, true) == null;
    }

    /**
     * 获取并移除一次 Skill 加载工具调用。
     *
     * @param toolCallId 工具调用 ID
     * @return Skill 加载调用
     */
    public AgentSkillLoadCall removeLoadCall(String toolCallId) {
        if (toolCallId == null) {
            return null;
        }
        emittedLoadCalls.remove(toolCallId);
        return pendingLoadCalls.remove(toolCallId);
    }

    private static String stringValue(Object value) {
        return value == null ? null : String.valueOf(value);
    }

    private static String displayName(AgentSkillSpec skillSpec) {
        Object value = skillSpec.getMetadata() == null ? null : skillSpec.getMetadata().get("displayName");
        return value == null || String.valueOf(value).isBlank()
                ? skillSpec.getName()
                : String.valueOf(value);
    }
}
