package tech.easyflow.agent.runtime;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.stereotype.Service;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.agent.entity.AgentKnowledgeBinding;
import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.enums.ResourceAction;
import tech.easyflow.system.service.ResourceAccessService;

import java.math.BigInteger;
import java.util.*;

/**
 * Agent 聊天临时能力编排服务。
 */
@Service
public class AgentChatCapabilityService {

    private static final String KNOWLEDGE_CAPABILITY_TYPE = "KNOWLEDGE";
    private static final String DEFAULT_RETRIEVAL_MODE = "HYBRID";
    private static final int MAX_EXTRA_KNOWLEDGE_COUNT = 3;

    private final DocumentCollectionService documentCollectionService;
    private final ResourceAccessService resourceAccessService;
    private final ObjectMapper objectMapper;

    /**
     * 创建 Agent 聊天临时能力编排服务。
     *
     * @param documentCollectionService 知识库服务
     * @param resourceAccessService 资源访问服务
     * @param objectMapper 对象复制工具
     */
    public AgentChatCapabilityService(DocumentCollectionService documentCollectionService,
                                      ResourceAccessService resourceAccessService,
                                      ObjectMapper objectMapper) {
        this.documentCollectionService = documentCollectionService;
        this.resourceAccessService = resourceAccessService;
        this.objectMapper = objectMapper;
    }

    /**
     * 将临时聊天能力合并到运行时 Agent 定义中。
     *
     * @param agent 已发布 Agent 运行视图
     * @param capabilities 临时能力请求
     * @param account 当前登录账号
     * @return 能力解析结果
     */
    public AgentChatCapabilityResolution apply(Agent agent,
                                               List<AgentChatCapability> capabilities,
                                               LoginAccount account) {
        List<BigInteger> extraKnowledgeIds = resolveKnowledgeIds(capabilities);
        boolean knowledgeCapabilityProvided = hasKnowledgeCapability(capabilities);
        if (agent == null || extraKnowledgeIds.isEmpty()) {
            return new AgentChatCapabilityResolution(agent, extraKnowledgeIds, knowledgeCapabilityProvided);
        }
        Agent runtimeAgent = objectMapper.convertValue(agent, Agent.class);
        List<AgentKnowledgeBinding> mergedBindings = new ArrayList<>();
        Set<BigInteger> existingKnowledgeIds = new LinkedHashSet<>();
        if (runtimeAgent.getKnowledgeBindings() != null) {
            for (AgentKnowledgeBinding binding : runtimeAgent.getKnowledgeBindings()) {
                if (binding == null) {
                    continue;
                }
                mergedBindings.add(binding);
                if (Boolean.TRUE.equals(binding.getEnabled()) && binding.getKnowledgeId() != null) {
                    existingKnowledgeIds.add(binding.getKnowledgeId());
                }
            }
        }
        int sortNo = mergedBindings.size();
        for (BigInteger knowledgeId : extraKnowledgeIds) {
            if (existingKnowledgeIds.contains(knowledgeId)) {
                continue;
            }
            DocumentCollection knowledge = documentCollectionService.getById(knowledgeId);
            validateKnowledge(knowledge);
            resourceAccessService.assertAccess(
                    CategoryResourceType.KNOWLEDGE,
                    knowledge,
                    ResourceAction.USE,
                    "无权限使用所选知识库"
            );
            AgentKnowledgeBinding binding = new AgentKnowledgeBinding();
            binding.setTenantId(account == null ? runtimeAgent.getTenantId() : account.getTenantId());
            binding.setAgentId(runtimeAgent.getId());
            binding.setKnowledgeId(knowledgeId);
            binding.setRetrievalMode(DEFAULT_RETRIEVAL_MODE);
            binding.setEnabled(true);
            binding.setSortNo(sortNo++);
            mergedBindings.add(binding);
            existingKnowledgeIds.add(knowledgeId);
        }
        runtimeAgent.setKnowledgeBindings(mergedBindings);
        return new AgentChatCapabilityResolution(runtimeAgent, extraKnowledgeIds, knowledgeCapabilityProvided);
    }

    /**
     * 从能力列表提取知识库 ID。
     *
     * @param capabilities 临时能力列表
     * @return 已去重知识库 ID
     */
    public List<BigInteger> resolveKnowledgeIds(List<AgentChatCapability> capabilities) {
        if (capabilities == null || capabilities.isEmpty()) {
            return List.of();
        }
        LinkedHashSet<BigInteger> ids = new LinkedHashSet<>();
        for (AgentChatCapability capability : capabilities) {
            if (capability == null || !isKnowledgeCapability(capability.getType())) {
                continue;
            }
            if (capability.getResourceIds() == null) {
                continue;
            }
            for (BigInteger resourceId : capability.getResourceIds()) {
                if (resourceId != null) {
                    ids.add(resourceId);
                }
            }
        }
        if (ids.size() > MAX_EXTRA_KNOWLEDGE_COUNT) {
            throw new BusinessException("临时知识库最多选择 3 个");
        }
        return new ArrayList<>(ids);
    }

    private boolean isKnowledgeCapability(String type) {
        return Objects.equals(KNOWLEDGE_CAPABILITY_TYPE, type == null ? null : type.trim().toUpperCase());
    }

    private boolean hasKnowledgeCapability(List<AgentChatCapability> capabilities) {
        if (capabilities == null || capabilities.isEmpty()) {
            return false;
        }
        for (AgentChatCapability capability : capabilities) {
            if (capability != null && isKnowledgeCapability(capability.getType())) {
                return true;
            }
        }
        return false;
    }

    private void validateKnowledge(DocumentCollection knowledge) {
        if (knowledge == null) {
            throw new BusinessException("所选知识库不存在");
        }
        if (PublishStatus.from(knowledge.getPublishStatus()) != PublishStatus.PUBLISHED) {
            throw new BusinessException("所选知识库未发布，无法用于聊天");
        }
    }

    /**
     * Agent 聊天临时能力解析结果。
     *
     * @param agent 合并临时能力后的运行时 Agent
     * @param extraKnowledgeIds 本次选择的临时知识库 ID
     * @param knowledgeCapabilityProvided 请求是否显式传入知识库能力
     */
    public record AgentChatCapabilityResolution(Agent agent,
                                                List<BigInteger> extraKnowledgeIds,
                                                boolean knowledgeCapabilityProvided) {
    }
}
