package tech.easyflow.agent.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.agent.config.AgentInteractionConfigSupport;
import tech.easyflow.agent.config.AgentBuiltinToolsConfigResolver;
import tech.easyflow.agent.config.AgentBuiltinToolsConfig;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
import tech.easyflow.agent.entity.AgentToolBinding;
import tech.easyflow.agent.entity.AgentSkillBinding;
import tech.easyflow.agent.mapper.AgentMapper;
import tech.easyflow.agent.runtime.AgentRuntimeCompiler;
import tech.easyflow.agent.service.AgentDependencyAccessService;
import tech.easyflow.agent.service.AgentKnowledgeBindingService;
import tech.easyflow.agent.service.AgentService;
import tech.easyflow.agent.service.AgentToolBindingService;
import tech.easyflow.agent.service.AgentSkillBindingService;
import tech.easyflow.agent.runtime.skill.AgentSkillRuntimeProjector;
import tech.easyflow.agent.support.AgentBindingLockExecutor;
import tech.easyflow.ai.entity.*;
import tech.easyflow.ai.easyagentsflow.repository.AgentWorkflowSnapshotFactory;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.mcp.McpConnectionSnapshotFactory;
import tech.easyflow.ai.plugin.PluginConnectionSnapshotFactory;
import tech.easyflow.ai.service.*;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.enums.ResourceAction;
import tech.easyflow.system.enums.VisibilityScope;
import tech.easyflow.system.entity.SysLog;
import tech.easyflow.system.service.ResourceAccessService;
import tech.easyflow.system.service.SysLogService;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Agent 配置服务实现。
 */
@Service
public class AgentServiceImpl extends ServiceImpl<AgentMapper, Agent> implements AgentService {

    private static final TypeReference<List<AgentToolBinding>> TOOL_BINDING_LIST_TYPE = new TypeReference<>() {};
    private static final TypeReference<List<AgentKnowledgeBinding>> KNOWLEDGE_BINDING_LIST_TYPE = new TypeReference<>() {};
    private static final TypeReference<List<AgentSkillBinding>> SKILL_BINDING_LIST_TYPE = new TypeReference<>() {};

    @Resource
    private AgentToolBindingService agentToolBindingService;
    @Resource
    private AgentKnowledgeBindingService agentKnowledgeBindingService;
    @Resource
    private AgentSkillBindingService agentSkillBindingService;
    @Resource
    private ModelService modelService;
    @Resource
    private WorkflowService workflowService;
    @Resource
    private PluginItemService pluginItemService;
    @Resource
    private McpService mcpService;
    @Resource
    private McpConnectionSnapshotFactory mcpConnectionSnapshotFactory;
    @Resource
    private PluginConnectionSnapshotFactory pluginConnectionSnapshotFactory;
    @Resource
    private DocumentCollectionService documentCollectionService;
    @Resource
    private ResourceAccessService resourceAccessService;
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private AgentDependencyAccessService agentDependencyAccessService;
    @Resource
    private AgentBindingLockExecutor agentBindingLockExecutor;
    @Resource
    private AgentRuntimeCompiler agentRuntimeCompiler;
    @Resource
    private AgentSkillRuntimeProjector agentSkillRuntimeProjector;
    @Resource
    private AgentWorkflowSnapshotFactory agentWorkflowSnapshotFactory;
    @Resource
    private AgentBuiltinToolsConfigResolver agentBuiltinToolsConfigResolver;
    @Resource
    private SysLogService sysLogService;

    /**
     * {@inheritDoc}
     */
    @Override
    public Agent getDetail(BigInteger id) {
        Agent agent = requireAgent(id);
        resourceAccessService.assertAccess(CategoryResourceType.AGENT, agent, ResourceAction.READ, "无权限查看该 Agent");
        agent.setExecutionConfigJson(
                agentBuiltinToolsConfigResolver.normalizeForDraftRead(agent.getExecutionConfigJson()));
        agent.setToolBindings(agentToolBindingService.listEnabled(id));
        agent.setKnowledgeBindings(agentKnowledgeBindingService.listEnabled(id));
        agent.setSkillBindings(agentSkillBindingService.listSummaries(id));
        return agent;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Agent saveDraft(Agent agent) {
        applyDraftDefaults(agent);
        validateDraft(agent, null);
        boolean shellApprovalDisabled = agentBuiltinToolsConfigResolver
                .isShellApprovalDisableTransition(agent.getExecutionConfigJson(), null);
        save(agent);
        if (shellApprovalDisabled) {
            recordShellApprovalDisabled(agent.getId(), "saveDraft",
                    AgentBuiltinToolsConfig.newAgentDefaults().shell());
        }
        return agent;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Agent updateDraft(Agent agent) {
        if (agent == null || agent.getId() == null) {
            throw new BusinessException("Agent ID 不能为空");
        }
        return agentBindingLockExecutor.execute(agent.getId(), () -> {
            Agent existing = requireAgentForUpdate(agent.getId());
            resourceAccessService.assertAccess(
                    CategoryResourceType.AGENT, existing, ResourceAction.MANAGE, "无权限管理该 Agent");
            agent.setTenantId(existing.getTenantId());
            Map<String, Object> existingExecutionConfig = existing.getExecutionConfigJson();
            validateDraft(agent, existingExecutionConfig);
            boolean shellApprovalDisabled = agentBuiltinToolsConfigResolver
                    .isShellApprovalDisableTransition(agent.getExecutionConfigJson(), existingExecutionConfig);
            AgentBuiltinToolsConfig.ToolSwitch previousShell = agentBuiltinToolsConfigResolver
                    .resolveDraftRuntime(existingExecutionConfig).shell();
            applyDraftUpdate(existing, agent);
            updateById(existing);
            if (shellApprovalDisabled) {
                recordShellApprovalDisabled(existing.getId(), "updateDraft", previousShell);
            }
            return existing;
        });
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Agent saveDraftGraph(Agent agent,
                                List<AgentToolBinding> toolBindings,
                                boolean replaceToolBindings,
                                List<AgentKnowledgeBinding> knowledgeBindings,
                                boolean replaceKnowledgeBindings,
                                List<AgentSkillBinding> skillBindings,
                                boolean replaceSkillBindings) {
        Agent saved = agent != null && agent.getId() != null
                ? updateDraft(agent) : saveDraft(agent);
        BigInteger agentId = saved.getId();
        if (replaceToolBindings) {
            saved.setToolBindings(agentToolBindingService.replaceBindings(agentId, toolBindings));
        }
        if (replaceKnowledgeBindings) {
            saved.setKnowledgeBindings(agentKnowledgeBindingService.replaceBindings(agentId, knowledgeBindings));
        }
        if (replaceSkillBindings) {
            saved.setSkillBindings(agentSkillBindingService.replaceBindings(agentId, skillBindings));
        }
        return saved;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Agent updateVisibilityScope(BigInteger agentId, String visibilityScope) {
        if (agentId == null) {
            throw new BusinessException("Agent ID 不能为空");
        }
        VisibilityScope scope = parseVisibilityScope(visibilityScope);
        return agentBindingLockExecutor.execute(agentId, () -> {
            Agent existing = requireAgentForUpdate(agentId);
            resourceAccessService.assertAccess(
                    CategoryResourceType.AGENT, existing, ResourceAction.MANAGE, "无权限管理该 Agent");
            LoginAccount account = requireCurrentLoginAccount();
            existing.setVisibilityScope(scope.name());
            existing.setModified(new Date());
            existing.setModifiedBy(account.getId());
            updateById(existing);
            return getDetail(existing.getId());
        });
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Agent getPublishedView(BigInteger id) {
        Agent agent = requireAgent(id);
        PublishStatus status = PublishStatus.from(agent.getPublishStatus());
        if (!status.isExternallyVisible() || agent.getPublishedSnapshotJson() == null || agent.getPublishedSnapshotJson().isEmpty()) {
            throw new BusinessException("Agent 未发布");
        }
        return fromSnapshot(agent.getPublishedSnapshotJson());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> buildPublishSnapshot(Agent agent) {
        if (agent == null || agent.getId() == null) {
            throw new BusinessException("Agent ID 不能为空");
        }
        return agentBindingLockExecutor.execute(agent.getId(), () -> buildPublishSnapshotLocked(agent.getId()));
    }

    /**
     * 在 Agent 锁和数据库行锁内构建并校验发布快照。
     *
     * @param agentId Agent ID
     * @return 发布快照
     */
    private Map<String, Object> buildPublishSnapshotLocked(BigInteger agentId) {
        Agent detail = requireAgentForUpdate(agentId);
        resourceAccessService.assertAccess(
                CategoryResourceType.AGENT, detail, ResourceAction.MANAGE, "无权限管理该 Agent");
        detail.setToolBindings(agentToolBindingService.listEnabled(agentId));
        detail.setKnowledgeBindings(agentKnowledgeBindingService.listEnabled(agentId));
        detail.setSkillBindings(agentSkillBindingService.listBindings(agentId));
        validateDraft(detail, detail.getExecutionConfigJson());
        List<AgentSkillBinding> projectedSkillBindings =
                agentSkillRuntimeProjector.projectCurrentBindings(detail, detail.getSkillBindings());
        Map<String, Object> snapshot = new LinkedHashMap<>();
        snapshot.put("id", detail.getId());
        snapshot.put("tenantId", detail.getTenantId());
        snapshot.put("deptId", detail.getDeptId());
        snapshot.put("createdBy", detail.getCreatedBy());
        snapshot.put("name", detail.getName());
        snapshot.put("description", detail.getDescription());
        snapshot.put("avatar", detail.getAvatar());
        snapshot.put("categoryId", detail.getCategoryId());
        snapshot.put("modelId", detail.getModelId());
        snapshot.put("modelConfigJson", detail.getModelConfigJson());
        snapshot.put("generationConfigJson", detail.getGenerationConfigJson());
        snapshot.put("promptConfigJson", detail.getPromptConfigJson());
        snapshot.put("memoryConfigJson", detail.getMemoryConfigJson());
        snapshot.put("executionConfigJson", detail.getExecutionConfigJson());
        snapshot.put("interactionConfigJson", detail.getInteractionConfigJson());
        snapshot.put("visibilityScope", detail.getVisibilityScope());
        snapshot.put("toolBindings", snapshotToolBindings(detail, detail.getToolBindings()));
        snapshot.put("knowledgeBindings", snapshotKnowledgeBindings(detail, detail.getKnowledgeBindings()));
        snapshot.put("skillBindings", projectedSkillBindings);
        snapshot.put("basicSummary", basicSummary(detail));
        snapshot.put("modelSummary", modelSummary(detail.getModelId()));
        snapshot.put("parameterSummary", parameterSummary(detail));
        snapshot.put("promptSummary", promptSummary(detail));
        snapshot.put("toolSummaries", toolSummaries(detail.getToolBindings()));
        snapshot.put("knowledgeSummaries", knowledgeSummaries(detail.getKnowledgeBindings()));
        snapshot.put("skillSummaries", projectedSkillBindings.stream()
                .map(AgentSkillBinding::getResourceSummary).toList());
        snapshot.put("snapshotAt", new Date());
        // 发布前完整编译一次，提前暴露工具运行名冲突和运行定义错误。
        agentRuntimeCompiler.compile(fromSnapshot(snapshot));
        return snapshot;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Agent fromSnapshot(Map<String, Object> snapshot) {
        if (snapshot == null || snapshot.isEmpty()) {
            throw new BusinessException("Agent 发布快照为空");
        }
        Agent agent = objectMapper.convertValue(snapshot, Agent.class);
        agent.setId(toBigInteger(snapshot.get("id")));
        agent.setTenantId(toBigInteger(snapshot.get("tenantId")));
        agent.setDeptId(toBigInteger(snapshot.get("deptId")));
        agent.setCreatedBy(toBigInteger(snapshot.get("createdBy")));
        agent.setModelId(toBigInteger(snapshot.get("modelId")));
        agent.setCategoryId(toBigInteger(snapshot.get("categoryId")));
        agent.setPublishStatus(PublishStatus.PUBLISHED.getCode());
        agent.setExecutionConfigJson(
                agentBuiltinToolsConfigResolver.normalizeForPublishedRuntime(agent.getExecutionConfigJson()));
        agent.setInteractionConfigJson(AgentInteractionConfigSupport.normalize(agent.getInteractionConfigJson()));
        agent.setPublishedSnapshotJson(snapshot);
        agent.setToolBindings(objectMapper.convertValue(snapshot.get("toolBindings"), TOOL_BINDING_LIST_TYPE));
        agent.setKnowledgeBindings(objectMapper.convertValue(snapshot.get("knowledgeBindings"), KNOWLEDGE_BINDING_LIST_TYPE));
        agent.setSkillBindings(snapshot.get("skillBindings") == null ? List.of()
                : objectMapper.convertValue(snapshot.get("skillBindings"), SKILL_BINDING_LIST_TYPE));
        return agent;
    }

    private Agent requireAgent(BigInteger id) {
        Agent agent = getById(id);
        if (agent == null) {
            throw new BusinessException("Agent 不存在");
        }
        return agent;
    }

    /**
     * 查询并锁定 Agent 数据行。
     *
     * @param id Agent ID
     * @return Agent
     */
    private Agent requireAgentForUpdate(BigInteger id) {
        Agent agent = getOne(QueryWrapper.create()
                .eq(Agent::getId, id)
                .forUpdate());
        if (agent == null) {
            throw new BusinessException("Agent 不存在");
        }
        return agent;
    }

    private void validateDraft(Agent agent, Map<String, Object> existingExecutionConfig) {
        if (agent == null) {
            throw new BusinessException("Agent 不能为空");
        }
        if (agent.getName() == null || agent.getName().isBlank()) {
            throw new BusinessException("Agent 名称不能为空");
        }
        agentDependencyAccessService.requireModel(agent, agent.getModelId());
        agentDependencyAccessService.validateCategory(agent);
        agent.setVisibilityScope(VisibilityScope.fromOrDefault(agent.getVisibilityScope(), VisibilityScope.PRIVATE).name());
        agent.setInteractionConfigJson(AgentInteractionConfigSupport.normalize(agent.getInteractionConfigJson()));
        Map<String, Object> executionConfig = normalizeExecutionConfig(agent.getExecutionConfigJson());
        agent.setExecutionConfigJson(agentBuiltinToolsConfigResolver.normalizeForDraftSave(
                executionConfig, existingExecutionConfig, requireCurrentLoginAccount()));
    }

    /**
     * 解析并校验 Agent 可见范围。
     *
     * @param visibilityScope 可见范围编码
     * @return 标准化后的可见范围
     */
    private VisibilityScope parseVisibilityScope(String visibilityScope) {
        try {
            return VisibilityScope.from(visibilityScope);
        } catch (IllegalArgumentException error) {
            throw new BusinessException(error.getMessage());
        }
    }

    /**
     * 规范并校验 Agent 运行配置中的文档上下文预算。
     *
     * @param source 原运行配置
     * @return 带默认预算的运行配置
     */
    private Map<String, Object> normalizeExecutionConfig(Map<String, Object> source) {
        Map<String, Object> normalized = source == null
                ? new LinkedHashMap<>() : new LinkedHashMap<>(source);
        Object value = normalized.get("documentContextBudgetTokens");
        if (value == null || String.valueOf(value).isBlank()) {
            normalized.put("documentContextBudgetTokens", 20_000);
            return normalized;
        }
        try {
            String text = String.valueOf(value).trim();
            if (!text.matches("\\d+")) {
                throw new NumberFormatException();
            }
            int budget = Integer.parseInt(text);
            if (budget <= 0) {
                throw new NumberFormatException();
            }
            normalized.put("documentContextBudgetTokens", budget);
            return normalized;
        } catch (NumberFormatException error) {
            throw new BusinessException("文档上下文预算必须为正整数");
        }
    }

    private void applyDraftDefaults(Agent agent) {
        LoginAccount account = requireCurrentLoginAccount();
        Date now = new Date();
        agent.setTenantId(account.getTenantId());
        agent.setDeptId(account.getDeptId());
        agent.setCreated(now);
        agent.setCreatedBy(account.getId());
        agent.setModified(now);
        agent.setModifiedBy(account.getId());
        agent.setStatus(agent.getStatus() == null ? 1 : agent.getStatus());
        agent.setPublishStatus(PublishStatus.DRAFT.getCode());
    }

    private Map<String, Object> basicSummary(Agent agent) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("id", agent.getId());
        summary.put("name", agent.getName());
        summary.put("description", agent.getDescription());
        summary.put("avatar", agent.getAvatar());
        summary.put("categoryId", agent.getCategoryId());
        summary.put("status", agent.getStatus());
        summary.put("publishStatus", PublishStatus.PUBLISHED.getCode());
        summary.put("visibilityScope", agent.getVisibilityScope());
        return summary;
    }

    private void applyDraftUpdate(Agent existing, Agent incoming) {
        LoginAccount account = requireCurrentLoginAccount();
        existing.setName(incoming.getName());
        existing.setDescription(incoming.getDescription());
        existing.setAvatar(incoming.getAvatar());
        existing.setCategoryId(incoming.getCategoryId());
        existing.setModelId(incoming.getModelId());
        existing.setModelConfigJson(incoming.getModelConfigJson());
        existing.setGenerationConfigJson(incoming.getGenerationConfigJson());
        existing.setPromptConfigJson(incoming.getPromptConfigJson());
        existing.setMemoryConfigJson(incoming.getMemoryConfigJson());
        existing.setExecutionConfigJson(incoming.getExecutionConfigJson());
        existing.setInteractionConfigJson(incoming.getInteractionConfigJson());
        existing.setStatus(incoming.getStatus() == null ? 1 : incoming.getStatus());
        existing.setVisibilityScope(incoming.getVisibilityScope());
        existing.setModified(new Date());
        existing.setModifiedBy(account.getId());
    }

    /**
     * 持久化 Shell 审批关闭这一高风险配置变更的专用审计记录。
     *
     * @param agentId Agent ID
     * @param actionMethod 触发变更的服务方法
     * @param previousShell 变更前 Shell 配置
     */
    private void recordShellApprovalDisabled(BigInteger agentId,
                                             String actionMethod,
                                             AgentBuiltinToolsConfig.ToolSwitch previousShell) {
        LoginAccount account = requireCurrentLoginAccount();
        SysLog log = new SysLog();
        log.setAccountId(account.getId());
        log.setActionName("关闭 Agent Shell 调用审批");
        log.setActionType("SECURITY_CONFIG_CHANGE");
        log.setActionClass(AgentServiceImpl.class.getName());
        log.setActionMethod(actionMethod);
        log.setActionUrl("/api/v1/agent/" + ("saveDraft".equals(actionMethod) ? "save" : "update"));
        log.setActionBody("{\"agentId\":\"" + agentId
                + "\",\"setting\":\"shell\",\"before\":{\"enabled\":"
                + previousShell.enabled() + ",\"approvalRequired\":" + previousShell.approvalRequired()
                + "},\"after\":{\"enabled\":true,\"approvalRequired\":false}}");
        log.setStatus(1);
        log.setCreated(new Date());
        sysLogService.save(log);
    }

    private Map<String, Object> modelSummary(BigInteger modelId) {
        Model model = modelService.getModelInstance(modelId);
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("id", model.getId());
        summary.put("title", model.getTitle());
        summary.put("modelName", model.getModelName());
        summary.put("providerType", model.getModelProvider() == null ? null : model.getModelProvider().getProviderType());
        summary.put("supportImage", Boolean.TRUE.equals(model.getSupportImage()));
        summary.put("supportImageB64Only", Boolean.TRUE.equals(model.getSupportImageB64Only()));
        return summary;
    }

    private Map<String, Object> parameterSummary(Agent agent) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("generationConfigJson", agent.getGenerationConfigJson());
        summary.put("modelConfigJson", agent.getModelConfigJson());
        summary.put("memoryConfigJson", agent.getMemoryConfigJson());
        summary.put("executionConfigJson", agent.getExecutionConfigJson());
        return summary;
    }

    private Map<String, Object> promptSummary(Agent agent) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("promptConfigJson", agent.getPromptConfigJson());
        summary.put("systemPrompt", agent.getPromptConfigJson() == null ? null : agent.getPromptConfigJson().get("systemPrompt"));
        summary.put("prompt", agent.getPromptConfigJson() == null ? null : agent.getPromptConfigJson().get("prompt"));
        return summary;
    }

    private List<Map<String, Object>> toolSummaries(List<AgentToolBinding> bindings) {
        if (bindings == null) {
            return List.of();
        }
        return bindings.stream().map(this::toolSummary).toList();
    }

    private Map<String, Object> toolSummary(AgentToolBinding binding) {
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("bindingId", binding.getId());
        summary.put("toolType", binding.getToolType());
        summary.put("targetId", binding.getTargetId());
        summary.put("enabled", Boolean.TRUE.equals(binding.getEnabled()));
        summary.put("hitlEnabled", Boolean.TRUE.equals(binding.getHitlEnabled()));
        summary.put("hitlConfigJson", binding.getHitlConfigJson());
        summary.put("sortNo", binding.getSortNo());
        if ("WORKFLOW".equalsIgnoreCase(binding.getToolType())) {
            summary.put("toolName", binding.getToolName());
            Workflow workflow = workflowService.getById(binding.getTargetId());
            summary.put("title", workflow == null ? null : workflow.getTitle());
        } else if ("PLUGIN".equalsIgnoreCase(binding.getToolType())) {
            summary.put("toolName", binding.getToolName());
            PluginItem pluginItem = pluginItemService.getById(binding.getTargetId());
            summary.put("title", pluginItem == null ? null : pluginItem.getName());
        } else {
            Mcp mcp = mcpService.getById(binding.getTargetId());
            summary.put("title", mcp == null ? null : mcp.getTitle());
            summary.put("tools", mcp == null || mcp.getTools() == null ? List.of() : mcp.getTools());
        }
        return summary;
    }

    private List<AgentToolBinding> snapshotToolBindings(Agent agent, List<AgentToolBinding> bindings) {
        if (bindings == null) {
            return List.of();
        }
        return bindings.stream().map(binding -> {
            AgentToolBinding snapshot = objectMapper.convertValue(binding, AgentToolBinding.class);
            snapshot.setResourceSnapshot(toolResourceSnapshot(agent, binding));
            snapshot.setResourceSummary(toolSummary(binding));
            return snapshot;
        }).toList();
    }

    private Map<String, Object> toolResourceSnapshot(Agent agent, AgentToolBinding binding) {
        if ("WORKFLOW".equalsIgnoreCase(binding.getToolType())) {
            Workflow workflow = agentDependencyAccessService.requireWorkflow(agent, binding.getTargetId());
            return agentWorkflowSnapshotFactory.snapshot(workflow);
        }
        if ("PLUGIN".equalsIgnoreCase(binding.getToolType())) {
            AgentDependencyAccessService.PluginResource resource =
                    agentDependencyAccessService.requirePluginResource(agent, binding.getTargetId());
            Map<String, Object> snapshot = new LinkedHashMap<>();
            snapshot.put("pluginItem", objectMapper.convertValue(
                    resource.pluginItem(), new TypeReference<Map<String, Object>>() {}));
            snapshot.put("plugin", pluginConnectionSnapshotFactory.snapshot(resource.plugin()));
            return snapshot;
        }
        Mcp mcp = agentDependencyAccessService.requireMcp(agent, binding.getTargetId());
        return mcpConnectionSnapshotFactory.snapshot(mcp);
    }

    private List<AgentKnowledgeBinding> snapshotKnowledgeBindings(
            Agent agent, List<AgentKnowledgeBinding> bindings) {
        if (bindings == null) {
            return List.of();
        }
        return bindings.stream().map(binding -> {
            AgentKnowledgeBinding snapshot = objectMapper.convertValue(binding, AgentKnowledgeBinding.class);
            snapshot.setResourceSnapshot(knowledgeResourceSnapshot(agent, binding));
            snapshot.setResourceSummary(knowledgeSummary(binding));
            return snapshot;
        }).toList();
    }

    private Map<String, Object> knowledgeResourceSnapshot(Agent agent, AgentKnowledgeBinding binding) {
        DocumentCollection knowledge =
                agentDependencyAccessService.requireKnowledge(agent, binding.getKnowledgeId());
        return objectMapper.convertValue(knowledge, new TypeReference<Map<String, Object>>() {});
    }

    private List<Map<String, Object>> knowledgeSummaries(List<AgentKnowledgeBinding> bindings) {
        if (bindings == null) {
            return List.of();
        }
        return bindings.stream().map(this::knowledgeSummary).toList();
    }

    private Map<String, Object> knowledgeSummary(AgentKnowledgeBinding binding) {
        DocumentCollection knowledge = documentCollectionService.getById(binding.getKnowledgeId());
        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("bindingId", binding.getId());
        summary.put("knowledgeId", binding.getKnowledgeId());
        summary.put("retrievalMode", binding.getRetrievalMode());
        summary.put("enabled", Boolean.TRUE.equals(binding.getEnabled()));
        summary.put("optionsJson", binding.getOptionsJson());
        summary.put("sortNo", binding.getSortNo());
        summary.put("title", knowledge == null ? null : knowledge.getTitle());
        return summary;
    }

    /**
     * 获取当前登录账号。
     *
     * @return 当前登录账号
     * @throws BusinessException 登录信息失效时抛出
     */
    private LoginAccount requireCurrentLoginAccount() {
        try {
            LoginAccount account = SaTokenUtil.getLoginAccount();
            if (account == null || account.getId() == null || account.getTenantId() == null) {
                throw new BusinessException("当前登录状态失效，请重新登录后再试");
            }
            return account;
        } catch (Exception e) {
            if (e instanceof BusinessException businessException) {
                throw businessException;
            }
            throw new BusinessException("当前登录状态失效，请重新登录后再试");
        }
    }

    private BigInteger toBigInteger(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof BigInteger bigInteger) {
            return bigInteger;
        }
        if (value instanceof Number number) {
            return BigInteger.valueOf(number.longValue());
        }
        return new BigInteger(String.valueOf(value));
    }
}
