package com.easyagents.agent.runtime.agentscope;

import com.easyagents.agent.runtime.AgentRuntimeException;
import com.easyagents.agent.runtime.skill.AgentSkillBoxSpec;
import com.easyagents.agent.runtime.skill.AgentSkillCompiler;
import com.easyagents.agent.runtime.skill.AgentSkillSpec;
import com.easyagents.agent.runtime.mcp.McpSkillRegistration;
import io.agentscope.core.skill.AgentSkill;
import io.agentscope.core.skill.SkillBox;
import io.agentscope.core.tool.AgentTool;
import io.agentscope.core.tool.Toolkit;

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

/**
 * 将运行时 Skill 适配为 AgentScope SkillBox。
 */
public class AgentScopeSkillAdapter implements AgentSkillCompiler<AgentSkill> {

    @Override
    public AgentSkill compile(AgentSkillSpec skillSpec) {
        validateSkillSpec(skillSpec);
        return new EasyAgentsAgentSkill(
                skillSpec.getSkillId(),
                skillSpec.getName(),
                skillSpec.getDescription(),
                skillSpec.getSkillContent(),
                skillSpec.getResources(),
                skillSpec.getSource());
    }

    /**
     * 创建并绑定 AgentScope SkillBox。
     *
     * @param spec SkillBox 声明
     * @param toolkit Toolkit 实例
     * @return SkillBox；未配置 Skill 时返回 null
     */
    public SkillBox createSkillBox(AgentSkillBoxSpec spec, Toolkit toolkit) {
        if (spec == null || spec.getSkills().isEmpty()) {
            return null;
        }
        SkillBox skillBox = spec.getSkillBoxId() == null || spec.getSkillBoxId().isEmpty()
                ? new SkillBox(toolkit)
                : new SkillBox(toolkit, spec.getSkillBoxId());
        skillBox.setExposeAllSkillMetadata(spec.isExposeAllSkillMetadata());
        for (AgentSkillSpec skillSpec : spec.getSkills()) {
            AgentSkill skill = compile(skillSpec);
            SkillBox.SkillRegistration registration = skillBox.registration()
                    .skill(skill)
                    .toolkit(toolkit)
                    .enableTools(spec.getEnabledToolNames())
                    .disableTools(spec.getDisabledToolNames())
                    .presetParameters(spec.getPresetParameters());
            registration.apply();
        }
        skillBox.syncToolGroupStates();
        return skillBox;
    }

    /**
     * 创建并绑定带 Skill 工具的 AgentScope SkillBox。
     *
     * @param spec SkillBox 声明
     * @param toolkit Toolkit 实例
     * @param skillTools 按 Skill ID 分组的工具
     * @return SkillBox；未配置 Skill 时返回 null
     */
    public SkillBox createSkillBox(AgentSkillBoxSpec spec, Toolkit toolkit, Map<String, List<AgentTool>> skillTools) {
        return createSkillBox(spec, toolkit, skillTools, List.of());
    }

    /**
     * 创建并绑定静态工具及 MCP 工具的 AgentScope SkillBox。
     *
     * @param spec SkillBox 声明
     * @param toolkit Toolkit 实例
     * @param skillTools 按 Skill ID 分组的静态工具
     * @param skillMcpRegistrations 按 Skill 延迟激活的 MCP client
     * @return SkillBox；未配置 Skill 时返回 null
     */
    public SkillBox createSkillBox(AgentSkillBoxSpec spec,
                                   Toolkit toolkit,
                                   Map<String, List<AgentTool>> skillTools,
                                   List<McpSkillRegistration> skillMcpRegistrations) {
        if (spec == null || spec.getSkills().isEmpty()) {
            return null;
        }
        SkillBox skillBox = spec.getSkillBoxId() == null || spec.getSkillBoxId().isEmpty()
                ? new SkillBox(toolkit)
                : new SkillBox(toolkit, spec.getSkillBoxId());
        skillBox.setExposeAllSkillMetadata(spec.isExposeAllSkillMetadata());
        Map<String, List<McpSkillRegistration>> mcpBySkill = groupMcpRegistrations(skillMcpRegistrations);
        for (AgentSkillSpec skillSpec : spec.getSkills()) {
            AgentSkill skill = compile(skillSpec);
            List<AgentTool> tools = skillTools == null ? List.of() : skillTools.getOrDefault(skillSpec.getSkillId(), List.of());
            List<McpSkillRegistration> mcpRegistrations = mcpBySkill.remove(skillSpec.getSkillId());
            if (mcpRegistrations == null) {
                mcpRegistrations = List.of();
            }
            if (tools.isEmpty() && mcpRegistrations.isEmpty()) {
                skillBox.registration()
                        .skill(skill)
                        .toolkit(toolkit)
                        .enableTools(spec.getEnabledToolNames())
                        .disableTools(spec.getDisabledToolNames())
                        .presetParameters(spec.getPresetParameters())
                        .apply();
                continue;
            }
            for (AgentTool tool : tools) {
                skillBox.registration()
                        .skill(skill)
                        .toolkit(toolkit)
                        .enableTools(spec.getEnabledToolNames())
                        .disableTools(spec.getDisabledToolNames())
                        .presetParameters(spec.getPresetParameters())
                        .agentTool(tool)
                        .apply();
            }
            for (McpSkillRegistration mcpRegistration : mcpRegistrations) {
                skillBox.registration()
                        .skill(skill)
                        .toolkit(toolkit)
                        .enableTools(mcpRegistration.getEnableTools())
                        .disableTools(mcpRegistration.getDisableTools())
                        .presetParameters(mcpRegistration.getPresetParameters())
                        .mcpClient(mcpRegistration.getClient())
                        .apply();
            }
        }
        if (!mcpBySkill.isEmpty()) {
            throw new AgentRuntimeException("Skill-bound MCP references unknown skill: "
                    + mcpBySkill.keySet().iterator().next());
        }
        skillBox.syncToolGroupStates();
        return skillBox;
    }

    private Map<String, List<McpSkillRegistration>> groupMcpRegistrations(
            List<McpSkillRegistration> registrations) {
        Map<String, List<McpSkillRegistration>> grouped = new LinkedHashMap<>();
        if (registrations == null) {
            return grouped;
        }
        for (McpSkillRegistration registration : registrations) {
            if (registration == null || registration.getSkillId() == null
                    || registration.getSkillId().isBlank()) {
                throw new AgentRuntimeException("Skill-bound MCP skill id is required.");
            }
            grouped.computeIfAbsent(registration.getSkillId(), key -> new java.util.ArrayList<>())
                    .add(registration);
        }
        return grouped;
    }

    /**
     * 校验 Skill 声明是否具备 AgentScope 注册和模型提示所需的必要信息。
     *
     * @param skillSpec Skill 声明
     * @throws AgentRuntimeException Skill 声明为空或核心字段缺失时抛出
     */
    private void validateSkillSpec(AgentSkillSpec skillSpec) {
        if (skillSpec == null) {
            throw new AgentRuntimeException("Agent skill spec is required.");
        }
        if (skillSpec.getSkillId() == null || skillSpec.getSkillId().isBlank()) {
            throw new AgentRuntimeException("Agent skill id is required.");
        }
        if (skillSpec.getName() == null || skillSpec.getName().isBlank()) {
            throw new AgentRuntimeException("Agent skill name is required: " + skillSpec.getSkillId());
        }
        if (skillSpec.getDescription() == null || skillSpec.getDescription().isBlank()) {
            throw new AgentRuntimeException("Agent skill description is required: " + skillSpec.getSkillId());
        }
        if (skillSpec.getSkillContent() == null || skillSpec.getSkillContent().isBlank()) {
            throw new AgentRuntimeException("Agent skill content is required: " + skillSpec.getSkillId());
        }
    }

    /**
     * 保持 Easy-Agents Skill ID 与 AgentScope Skill ID 完全一致。
     *
     * <p>AgentScope 默认用 {@code name + "_" + source} 生成 Skill ID，而 Easy-Agents
     * 的工具绑定、旁路事件和调用层配置都以 {@link AgentSkillSpec#getSkillId()} 为准。
     * 如果不覆盖这里，模型在 prompt 中看到的 skill-id 会和 Easy 侧绑定 key 不一致，
     * 后续 Skill 状态监听也无法做到精准归属。</p>
     */
    private static class EasyAgentsAgentSkill extends AgentSkill {

        private final String skillId;

        /**
         * 创建 ID 对齐的 AgentScope Skill。
         *
         * @param skillId Easy-Agents Skill ID
         * @param name Skill 展示名称
         * @param description Skill 描述
         * @param skillContent Skill 内容
         * @param resources Skill 资源
         * @param source Skill 来源
         */
        EasyAgentsAgentSkill(String skillId,
                             String name,
                             String description,
                             String skillContent,
                             Map<String, String> resources,
                             String source) {
            super(name, description, skillContent, resources, source);
            this.skillId = skillId;
        }

        /**
         * 获取 Easy-Agents 声明的 Skill ID。
         *
         * @return Easy-Agents Skill ID
         */
        @Override
        public String getSkillId() {
            return skillId;
        }
    }
}
