package tech.easyflow.agent.service.impl;

import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
import tech.easyflow.agent.entity.AgentToolBinding;
import tech.easyflow.agent.enums.AgentToolType;
import tech.easyflow.agent.service.AgentKnowledgeBindingService;
import tech.easyflow.agent.service.AgentService;
import tech.easyflow.agent.service.AgentToolBindingService;
import tech.easyflow.agent.support.AgentBindingLockExecutor;
import tech.easyflow.ai.service.AgentResourceBindingProvider;
import tech.easyflow.ai.vo.OfflineImpactBindingVo;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Agent 对共享 AI 资源的绑定查询与解绑实现。
 */
@Service
public class AgentResourceBindingProviderImpl implements AgentResourceBindingProvider {

    private final AgentService agentService;
    private final AgentToolBindingService agentToolBindingService;
    private final AgentKnowledgeBindingService agentKnowledgeBindingService;
    private final AgentBindingLockExecutor agentBindingLockExecutor;

    /**
     * 创建 Agent 资源绑定提供者。
     *
     * @param agentService Agent 服务
     * @param agentToolBindingService Agent 工具绑定服务
     * @param agentKnowledgeBindingService Agent 知识库绑定服务
     * @param agentBindingLockExecutor Agent 绑定锁执行器
     */
    public AgentResourceBindingProviderImpl(AgentService agentService,
                                            AgentToolBindingService agentToolBindingService,
                                            AgentKnowledgeBindingService agentKnowledgeBindingService,
                                            AgentBindingLockExecutor agentBindingLockExecutor) {
        this.agentService = agentService;
        this.agentToolBindingService = agentToolBindingService;
        this.agentKnowledgeBindingService = agentKnowledgeBindingService;
        this.agentBindingLockExecutor = agentBindingLockExecutor;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<OfflineImpactBindingVo> listAgentsByWorkflowId(BigInteger workflowId) {
        return listAgents(collectToolResourceAgentIds(AgentToolType.WORKFLOW, workflowId, false));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<OfflineImpactBindingVo> listAgentsByKnowledgeId(BigInteger knowledgeId) {
        return listAgents(collectKnowledgeAgentIds(knowledgeId, false));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<OfflineImpactBindingVo> listAgentsByPluginItemId(BigInteger pluginItemId) {
        return listAgents(collectToolResourceAgentIds(AgentToolType.PLUGIN, pluginItemId, false));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<OfflineImpactBindingVo> listAgentsByMcpId(BigInteger mcpId) {
        return listAgents(collectToolResourceAgentIds(AgentToolType.MCP, mcpId, false));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<OfflineImpactBindingVo> listAgentsByModelId(BigInteger modelId) {
        Set<BigInteger> agentIds = collectAgentIdsFromAgents(agentService.list(QueryWrapper.create()
                .select(Agent::getId)
                .eq(Agent::getModelId, modelId)));
        for (Agent agent : listPublishedSnapshotAgents()) {
            if (sameId(agent.getPublishedSnapshotJson().get("modelId"), modelId)) {
                agentIds.add(agent.getId());
            }
        }
        return listAgents(agentIds);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unbindWorkflow(BigInteger workflowId) {
        Set<BigInteger> agentIds = collectToolResourceAgentIds(
                AgentToolType.WORKFLOW, workflowId, true);
        for (BigInteger agentId : sortedAgentIds(agentIds)) {
            agentBindingLockExecutor.execute(agentId, () -> {
                agentToolBindingService.remove(QueryWrapper.create()
                        .eq(AgentToolBinding::getAgentId, agentId)
                        .eq(AgentToolBinding::getToolType, AgentToolType.WORKFLOW.name())
                        .eq(AgentToolBinding::getTargetId, workflowId));
                trimPublishedSnapshot(
                        agentId,
                        workflowId,
                        AgentToolType.WORKFLOW.name(),
                        "toolBindings",
                        "toolSummaries"
                );
                return null;
            });
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unbindKnowledge(BigInteger knowledgeId) {
        Set<BigInteger> agentIds = collectKnowledgeAgentIds(knowledgeId, true);
        for (BigInteger agentId : sortedAgentIds(agentIds)) {
            agentBindingLockExecutor.execute(agentId, () -> {
                agentKnowledgeBindingService.remove(QueryWrapper.create()
                        .eq(AgentKnowledgeBinding::getAgentId, agentId)
                        .eq(AgentKnowledgeBinding::getKnowledgeId, knowledgeId));
                trimPublishedSnapshot(
                        agentId,
                        knowledgeId,
                        null,
                        "knowledgeBindings",
                        "knowledgeSummaries"
                );
                return null;
            });
        }
    }

    /**
     * 查询 Agent 摘要并保持绑定中首次出现的顺序。
     *
     * @param agentIds Agent ID 集合
     * @return Agent 摘要列表
     */
    private List<OfflineImpactBindingVo> listAgents(Set<BigInteger> agentIds) {
        if (agentIds.isEmpty()) {
            return Collections.emptyList();
        }
        Map<BigInteger, Agent> agentMap = new LinkedHashMap<>();
        for (Agent agent : agentService.listByIds(agentIds)) {
            agentMap.put(agent.getId(), agent);
        }
        List<OfflineImpactBindingVo> result = new ArrayList<>(agentIds.size());
        for (BigInteger agentId : agentIds) {
            Agent agent = agentMap.get(agentId);
            OfflineImpactBindingVo item = new OfflineImpactBindingVo();
            item.setId(agentId);
            item.setTitle(agent == null ? "已删除智能体（悬空绑定）" : agent.getName());
            result.add(item);
        }
        return result;
    }

    /**
     * 收集草稿绑定和已发布快照中引用指定工具资源的 Agent ID。
     *
     * @param toolType 工具类型
     * @param resourceId 资源 ID
     * @param lockBindings 是否锁定实时绑定行
     * @return Agent ID 集合
     */
    private Set<BigInteger> collectToolResourceAgentIds(AgentToolType toolType,
                                                        BigInteger resourceId,
                                                        boolean lockBindings) {
        QueryWrapper wrapper = QueryWrapper.create()
                .eq(AgentToolBinding::getToolType, toolType.name())
                .eq(AgentToolBinding::getTargetId, resourceId);
        if (lockBindings) {
            wrapper.forUpdate();
        }
        Set<BigInteger> agentIds = collectAgentIdsFromToolBindings(
                agentToolBindingService.list(wrapper));
        for (Agent agent : listPublishedSnapshotAgents()) {
            if (snapshotContainsToolResource(agent.getPublishedSnapshotJson(), toolType, resourceId)) {
                agentIds.add(agent.getId());
            }
        }
        return agentIds;
    }

    /**
     * 收集草稿绑定和已发布快照中引用指定知识库的 Agent ID。
     *
     * @param knowledgeId 知识库 ID
     * @param lockBindings 是否锁定实时绑定行
     * @return Agent ID 集合
     */
    private Set<BigInteger> collectKnowledgeAgentIds(BigInteger knowledgeId, boolean lockBindings) {
        QueryWrapper wrapper = QueryWrapper.create()
                .eq(AgentKnowledgeBinding::getKnowledgeId, knowledgeId);
        if (lockBindings) {
            wrapper.forUpdate();
        }
        Set<BigInteger> agentIds = collectAgentIdsFromKnowledgeBindings(
                agentKnowledgeBindingService.list(wrapper));
        for (Agent agent : listPublishedSnapshotAgents()) {
            if (snapshotContainsKnowledge(agent.getPublishedSnapshotJson(), knowledgeId)) {
                agentIds.add(agent.getId());
            }
        }
        return agentIds;
    }

    /**
     * 查询带发布快照的 Agent 最小字段。
     *
     * @return Agent 发布快照记录
     */
    private List<Agent> listPublishedSnapshotAgents() {
        return agentService.list(QueryWrapper.create()
                .select(
                        Agent::getId,
                        Agent::getName,
                        Agent::getModelId,
                        Agent::getPublishedSnapshotJson
                )
                .isNotNull(Agent::getPublishedSnapshotJson));
    }

    /**
     * 判断发布快照是否引用指定工具资源。
     *
     * @param snapshot 发布快照
     * @param toolType 工具类型
     * @param resourceId 资源 ID
     * @return 是否引用
     */
    private boolean snapshotContainsToolResource(Map<String, Object> snapshot,
                                                 AgentToolType toolType,
                                                 BigInteger resourceId) {
        return snapshotListContains(snapshot, "toolBindings", resourceId, toolType.name())
                || snapshotListContains(snapshot, "toolSummaries", resourceId, toolType.name())
                || nestedSkillBindingsContain(snapshot, toolType, resourceId);
    }

    private boolean nestedSkillBindingsContain(Map<String, Object> snapshot,
                                               AgentToolType toolType,
                                               BigInteger resourceId) {
        Object rawBindings = snapshot == null ? null : snapshot.get("skillBindings");
        if (!(rawBindings instanceof List<?> bindings)) {
            return false;
        }
        for (Object raw : bindings) {
            if (!(raw instanceof Map<?, ?> binding)
                    || !(binding.get("resourceSnapshot") instanceof Map<?, ?> resourceSnapshot)) {
                continue;
            }
            Object rawTools = resourceSnapshot.get("toolBindings");
            if (rawTools instanceof List<?> tools
                    && tools.stream().anyMatch(item -> matchesResourceBinding(item, resourceId, toolType.name()))) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断发布快照是否引用指定知识库。
     *
     * @param snapshot 发布快照
     * @param knowledgeId 知识库 ID
     * @return 是否引用
     */
    private boolean snapshotContainsKnowledge(Map<String, Object> snapshot, BigInteger knowledgeId) {
        return snapshotListContains(snapshot, "knowledgeBindings", knowledgeId, null)
                || snapshotListContains(snapshot, "knowledgeSummaries", knowledgeId, null);
    }

    /**
     * 判断快照列表中是否存在指定资源。
     *
     * @param snapshot 发布快照
     * @param key 列表字段
     * @param resourceId 资源 ID
     * @param toolType 工具类型；知识库为空
     * @return 是否存在
     */
    private boolean snapshotListContains(Map<String, Object> snapshot,
                                         String key,
                                         BigInteger resourceId,
                                         String toolType) {
        if (snapshot == null || snapshot.isEmpty()) {
            return false;
        }
        Object value = snapshot.get(key);
        if (!(value instanceof List<?> items)) {
            return false;
        }
        return items.stream().anyMatch(item -> matchesResourceBinding(item, resourceId, toolType));
    }

    /**
     * 收集工具绑定中的 Agent ID。
     *
     * @param bindings 工具绑定
     * @return 去重后的 Agent ID
     */
    private Set<BigInteger> collectAgentIdsFromToolBindings(List<AgentToolBinding> bindings) {
        Set<BigInteger> result = new LinkedHashSet<>();
        if (bindings == null) {
            return result;
        }
        for (AgentToolBinding binding : bindings) {
            if (binding != null && binding.getAgentId() != null) {
                result.add(binding.getAgentId());
            }
        }
        return result;
    }

    /**
     * 收集知识库绑定中的 Agent ID。
     *
     * @param bindings 知识库绑定
     * @return 去重后的 Agent ID
     */
    private Set<BigInteger> collectAgentIdsFromKnowledgeBindings(List<AgentKnowledgeBinding> bindings) {
        Set<BigInteger> result = new LinkedHashSet<>();
        if (bindings == null) {
            return result;
        }
        for (AgentKnowledgeBinding binding : bindings) {
            if (binding != null && binding.getAgentId() != null) {
                result.add(binding.getAgentId());
            }
        }
        return result;
    }

    /**
     * 收集 Agent 实体中的 ID。
     *
     * @param agents Agent 列表
     * @return Agent ID 集合
     */
    private Set<BigInteger> collectAgentIdsFromAgents(List<Agent> agents) {
        Set<BigInteger> result = new LinkedHashSet<>();
        if (agents == null) {
            return result;
        }
        for (Agent agent : agents) {
            if (agent != null && agent.getId() != null) {
                result.add(agent.getId());
            }
        }
        return result;
    }

    /**
     * 按 Agent ID 升序返回锁定顺序，避免并发批量解绑以相反顺序持锁。
     *
     * @param agentIds Agent ID 集合
     * @return 稳定排序后的 Agent ID
     */
    private List<BigInteger> sortedAgentIds(Set<BigInteger> agentIds) {
        if (agentIds == null || agentIds.isEmpty()) {
            return Collections.emptyList();
        }
        return agentIds.stream()
                .sorted(Comparator.naturalOrder())
                .toList();
    }

    /**
     * 从 Agent 发布快照中移除指定资源绑定。
     *
     * @param agentId Agent ID
     * @param resourceId 资源 ID
     * @param toolType 工具类型；知识库绑定时为空
     * @param bindingsKeys 快照绑定字段
     */
    private void trimPublishedSnapshot(BigInteger agentId,
                                       BigInteger resourceId,
                                       String toolType,
                                       String... bindingsKeys) {
        Agent agent = agentService.getOne(QueryWrapper.create()
                .eq(Agent::getId, agentId)
                .forUpdate());
        if (agent == null || agent.getPublishedSnapshotJson() == null || agent.getPublishedSnapshotJson().isEmpty()) {
            return;
        }
        Map<String, Object> snapshot = new LinkedHashMap<>(agent.getPublishedSnapshotJson());
        boolean changed = false;
        for (String bindingsKey : bindingsKeys) {
            Object rawBindings = snapshot.get(bindingsKey);
            if (!(rawBindings instanceof List<?> bindings)) {
                continue;
            }
            List<Object> filtered = new ArrayList<>(bindings.size());
            for (Object item : bindings) {
                if (matchesResourceBinding(item, resourceId, toolType)) {
                    changed = true;
                    continue;
                }
                filtered.add(item);
            }
            snapshot.put(bindingsKey, filtered);
        }
        if (!changed) {
            return;
        }
        // 仅更新发布快照，避免并发草稿编辑被旧实体中的其他字段覆盖。
        agentService.updateChain()
                .set(Agent::getPublishedSnapshotJson, snapshot)
                .eq(Agent::getId, agentId)
                .update();
    }

    /**
     * 判断快照项是否指向指定资源。
     *
     * @param item 快照项
     * @param resourceId 资源 ID
     * @param toolType 工具类型；知识库绑定时为空
     * @return 是否匹配
     */
    private boolean matchesResourceBinding(Object item, BigInteger resourceId, String toolType) {
        if (!(item instanceof Map<?, ?> binding)) {
            return false;
        }
        Object currentId = toolType == null ? binding.get("knowledgeId") : binding.get("targetId");
        if (!Objects.equals(String.valueOf(currentId), String.valueOf(resourceId))) {
            return false;
        }
        return toolType == null || toolType.equalsIgnoreCase(String.valueOf(binding.get("toolType")));
    }

    /**
     * 比较快照中的资源 ID 与数据库 ID。
     *
     * @param snapshotId 快照 ID
     * @param resourceId 数据库 ID
     * @return 是否相同
     */
    private boolean sameId(Object snapshotId, BigInteger resourceId) {
        return snapshotId != null
                && resourceId != null
                && Objects.equals(String.valueOf(snapshotId), resourceId.toString());
    }
}
