package tech.easyflow.skill.service;

import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.ai.entity.Mcp;
import tech.easyflow.ai.entity.Plugin;
import tech.easyflow.ai.entity.PluginItem;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.mapper.PluginMapper;
import tech.easyflow.ai.permission.McpAccessPermissionChecker;
import tech.easyflow.ai.service.McpService;
import tech.easyflow.ai.service.PluginItemService;
import tech.easyflow.ai.service.PluginVisibilityService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.skill.entity.Skill;
import tech.easyflow.skill.entity.SkillToolBinding;
import tech.easyflow.skill.vo.SkillMcpToolManifestView;
import tech.easyflow.skill.vo.SkillToolOptionPage;
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.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;

/**
 * Skill Studio 页面归属的 Tool 候选与 MCP 清单查询服务。
 */
@Service
public class SkillToolOptionQueryService {

    private final WorkflowService workflowService;
    private final PluginItemService pluginItemService;
    private final PluginMapper pluginMapper;
    private final PluginVisibilityService pluginVisibilityService;
    private final McpService mcpService;
    private final McpAccessPermissionChecker mcpAccessPermissionChecker;
    private final SkillToolResourceService resourceService;
    private final ResourceAccessService resourceAccessService;

    /**
     * 创建候选查询服务。
     */
    public SkillToolOptionQueryService(WorkflowService workflowService,
                                       PluginItemService pluginItemService,
                                       PluginMapper pluginMapper,
                                       PluginVisibilityService pluginVisibilityService,
                                       McpService mcpService,
                                       McpAccessPermissionChecker mcpAccessPermissionChecker,
                                       SkillToolResourceService resourceService,
                                       ResourceAccessService resourceAccessService) {
        this.workflowService = workflowService;
        this.pluginItemService = pluginItemService;
        this.pluginMapper = pluginMapper;
        this.pluginVisibilityService = pluginVisibilityService;
        this.mcpService = mcpService;
        this.mcpAccessPermissionChecker = mcpAccessPermissionChecker;
        this.resourceService = resourceService;
        this.resourceAccessService = resourceAccessService;
    }

    /**
     * 查询当前操作者可绑定的 Tool 候选。
     *
     * @param keyword 名称或描述关键词
     * @param toolType 类型过滤
     * @param pageNum 页码
     * @param pageSize 每页数量
     * @return 安全候选分页
     */
    public SkillToolOptionPage page(String keyword, String toolType, long pageNum, long pageSize) {
        LoginAccount account = requireAccount();
        String normalizedType = toolType == null ? "ALL" : toolType.trim().toUpperCase(Locale.ROOT);
        if (!List.of("ALL", "WORKFLOW", "PLUGIN", "MCP").contains(normalizedType)) {
            throw new BusinessException("不支持的 Tool 类型：" + toolType);
        }
        Predicate<SkillToolOptionPage.Item> keywordFilter = item -> matches(item, keyword);
        List<SkillToolOptionPage.Item> candidates = new ArrayList<>();
        if ("ALL".equals(normalizedType) || "WORKFLOW".equals(normalizedType)) {
            workflowService.list(QueryWrapper.create()
                            .eq(Workflow::getTenantId, account.getTenantId())
                            .eq(Workflow::getPublishStatus, PublishStatus.PUBLISHED.getCode()))
                    .stream().filter(item -> resourceAccessService.canAccess(
                            CategoryResourceType.WORKFLOW, item, ResourceAction.USE))
                    .map(item -> new SkillToolOptionPage.Item("WORKFLOW", item.getId(), item.getTitle(),
                            item.getDescription(), true, false, 1))
                    .filter(keywordFilter).forEach(candidates::add);
        }
        if ("ALL".equals(normalizedType) || "PLUGIN".equals(normalizedType)) {
            appendPlugins(account, keywordFilter, candidates);
        }
        if ("MCP".equals(normalizedType)) {
            mcpAccessPermissionChecker.assertCanUseMcp();
            appendMcps(account, keywordFilter, candidates);
        } else if ("ALL".equals(normalizedType) && mcpAccessPermissionChecker.canUseMcp()) {
            // 聚合查询只展示当前用户可用的资源，不能让缺少 MCP 权限影响其他候选。
            appendMcps(account, keywordFilter, candidates);
        }
        candidates.sort(Comparator.comparing(SkillToolOptionPage.Item::title,
                Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))
                .thenComparing(SkillToolOptionPage.Item::targetId));
        long safePage = Math.max(1, pageNum);
        long safeSize = Math.max(1, Math.min(100, pageSize));
        int from = (int) Math.min(candidates.size(), (safePage - 1) * safeSize);
        int to = (int) Math.min(candidates.size(), from + safeSize);
        return new SkillToolOptionPage(candidates.subList(from, to), candidates.size(), safePage, safeSize);
    }

    /**
     * 追加当前租户可用的 MCP 候选。
     *
     * @param account 当前账号
     * @param keywordFilter 关键词过滤器
     * @param candidates 候选集合
     */
    private void appendMcps(LoginAccount account,
                            Predicate<SkillToolOptionPage.Item> keywordFilter,
                            List<SkillToolOptionPage.Item> candidates) {
        mcpService.list(QueryWrapper.create()
                        .eq(Mcp::getTenantId, account.getTenantId())
                        .eq(Mcp::getStatus, true))
                .stream().map(item -> new SkillToolOptionPage.Item("MCP", item.getId(), item.getTitle(),
                        item.getDescription(), true, Boolean.TRUE.equals(item.getApprovalRequired()), null))
                .filter(keywordFilter).forEach(candidates::add);
    }

    /**
     * 发现指定 MCP 的 Tool 清单。
     *
     * @param mcpId MCP ID
     * @return 脱敏清单
     */
    @Transactional(rollbackFor = Exception.class)
    public SkillMcpToolManifestView mcpTools(BigInteger mcpId) {
        LoginAccount account = requireAccount();
        Skill pseudoSkill = new Skill();
        pseudoSkill.setTenantId(account.getTenantId());
        SkillToolBinding binding = new SkillToolBinding();
        binding.setToolType("MCP");
        binding.setTargetId(mcpId);
        SkillToolResourceService.McpResource resource = resourceService.requireMcp(pseudoSkill, binding);
        List<SkillMcpToolManifestView.Tool> tools = resource.manifest().stream()
                .map(item -> new SkillMcpToolManifestView.Tool(item.getName(), item.getDescription(),
                        item.getInputSchema(), item.getOutputSchema()))
                .toList();
        return new SkillMcpToolManifestView(resource.manifestHash(), tools.size(), tools);
    }

    private void appendPlugins(LoginAccount account,
                               Predicate<SkillToolOptionPage.Item> keywordFilter,
                               List<SkillToolOptionPage.Item> candidates) {
        Map<BigInteger, Plugin> plugins = pluginMapper.selectListByQuery(QueryWrapper.create()
                        .eq(Plugin::getTenantId, account.getTenantId())).stream()
                .filter(plugin -> pluginVisibilityService.canAccessPlugin(plugin.getCreatedBy(), plugin.getId()))
                .collect(java.util.stream.Collectors.toMap(Plugin::getId, plugin -> plugin));
        if (plugins.isEmpty()) {
            return;
        }
        pluginItemService.list(QueryWrapper.create()
                        .in(PluginItem::getPluginId, plugins.keySet())
                        .eq(PluginItem::getStatus, 1))
                .stream().map(item -> new SkillToolOptionPage.Item("PLUGIN", item.getId(), item.getName(),
                        item.getDescription(), true, false, 1))
                .filter(keywordFilter).forEach(candidates::add);
    }

    private boolean matches(SkillToolOptionPage.Item item, String keyword) {
        if (keyword == null || keyword.isBlank()) {
            return true;
        }
        String needle = keyword.trim().toLowerCase(Locale.ROOT);
        return contains(item.title(), needle) || contains(item.description(), needle);
    }

    private boolean contains(String value, String needle) {
        return value != null && value.toLowerCase(Locale.ROOT).contains(needle);
    }

    private LoginAccount requireAccount() {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        if (account == null || account.getId() == null || account.getTenantId() == null) {
            throw new BusinessException(401, 401, "当前登录状态失效，请重新登录后再试");
        }
        return account;
    }
}
