package tech.easyflow.agent.service.impl;

import com.mybatisflex.core.query.QueryWrapper;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.agent.service.AgentCategoryService;
import tech.easyflow.agent.service.AgentDependencyAccessService;
import tech.easyflow.ai.entity.DocumentCollection;
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.service.DocumentCollectionService;
import tech.easyflow.ai.service.McpService;
import tech.easyflow.ai.service.ModelService;
import tech.easyflow.ai.service.PluginItemService;
import tech.easyflow.ai.service.PluginVisibilityService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.system.service.CategoryPermissionService;
import tech.easyflow.system.service.ResourceAccessService;

import java.math.BigInteger;
import java.util.Locale;

/**
 * Agent 绑定资源状态锁测试。
 */
public class AgentBindingValidationLockTest {

    /**
     * 验证工作流绑定使用锁定读校验最新发布状态。
     *
     * @throws Exception 反射调用失败
     */
    @Test
    public void workflowBindingShouldValidateWithForUpdate() {
        Workflow workflow = new Workflow();
        workflow.setPublishStatus(PublishStatus.PUBLISHED.getCode());
        workflow.setTenantId(BigInteger.ONE);
        WorkflowService workflowService = Mockito.mock(WorkflowService.class);
        Mockito.when(workflowService.getOne(Mockito.any(QueryWrapper.class))).thenReturn(workflow);
        AgentDependencyAccessService service = createService(
                workflowService,
                Mockito.mock(PluginItemService.class),
                Mockito.mock(PluginMapper.class),
                Mockito.mock(PluginVisibilityService.class),
                Mockito.mock(McpService.class),
                Mockito.mock(DocumentCollectionService.class),
                Mockito.mock(ResourceAccessService.class)
        );

        service.requireWorkflow(agent(), BigInteger.valueOf(1001));

        ArgumentCaptor<QueryWrapper> queryCaptor = ArgumentCaptor.forClass(QueryWrapper.class);
        Mockito.verify(workflowService).getOne(queryCaptor.capture());
        Assert.assertTrue(queryCaptor.getValue().toSQL().toUpperCase(Locale.ROOT).contains("FOR UPDATE"));
    }

    /**
     * 验证知识库绑定使用锁定读校验最新发布状态。
     *
     * @throws Exception 反射调用失败
     */
    @Test
    public void knowledgeBindingShouldValidateWithForUpdate() {
        DocumentCollection knowledge = new DocumentCollection();
        knowledge.setPublishStatus(PublishStatus.PUBLISHED.getCode());
        knowledge.setTenantId(BigInteger.ONE);
        DocumentCollectionService knowledgeService = Mockito.mock(DocumentCollectionService.class);
        Mockito.when(knowledgeService.getOne(Mockito.any(QueryWrapper.class))).thenReturn(knowledge);
        AgentDependencyAccessService service = createService(
                Mockito.mock(WorkflowService.class),
                Mockito.mock(PluginItemService.class),
                Mockito.mock(PluginMapper.class),
                Mockito.mock(PluginVisibilityService.class),
                Mockito.mock(McpService.class),
                knowledgeService,
                Mockito.mock(ResourceAccessService.class)
        );

        service.requireKnowledge(agent(), BigInteger.valueOf(2001));

        ArgumentCaptor<QueryWrapper> queryCaptor = ArgumentCaptor.forClass(QueryWrapper.class);
        Mockito.verify(knowledgeService).getOne(queryCaptor.capture());
        Assert.assertTrue(queryCaptor.getValue().toSQL().toUpperCase(Locale.ROOT).contains("FOR UPDATE"));
    }

    /**
     * 验证插件绑定使用锁定读校验最新启用状态。
     *
     * @throws Exception 反射调用失败
     */
    @Test
    public void pluginBindingShouldValidateWithForUpdate() {
        BigInteger pluginId = BigInteger.valueOf(30);
        BigInteger pluginItemId = BigInteger.valueOf(3001);
        PluginItem pluginItem = pluginItem(pluginId);
        PluginItemService pluginItemService = Mockito.mock(PluginItemService.class);
        Mockito.when(pluginItemService.getById(pluginItemId)).thenReturn(pluginItem);
        Mockito.when(pluginItemService.getOne(Mockito.any(QueryWrapper.class))).thenReturn(pluginItem);
        Plugin plugin = new Plugin();
        plugin.setId(pluginId);
        plugin.setTenantId(1L);
        PluginMapper pluginMapper = Mockito.mock(PluginMapper.class);
        Mockito.when(pluginMapper.selectOneByQuery(Mockito.any(QueryWrapper.class))).thenReturn(plugin);
        AgentDependencyAccessService service = createService(
                Mockito.mock(WorkflowService.class),
                pluginItemService,
                pluginMapper,
                Mockito.mock(PluginVisibilityService.class),
                Mockito.mock(McpService.class),
                Mockito.mock(DocumentCollectionService.class),
                Mockito.mock(ResourceAccessService.class)
        );

        service.requirePluginItem(agent(), pluginItemId);

        ArgumentCaptor<QueryWrapper> pluginQueryCaptor = ArgumentCaptor.forClass(QueryWrapper.class);
        Mockito.verify(pluginMapper).selectOneByQuery(pluginQueryCaptor.capture());
        Assert.assertTrue(pluginQueryCaptor.getValue().toSQL().toUpperCase(Locale.ROOT).contains("FOR UPDATE"));
        ArgumentCaptor<QueryWrapper> itemQueryCaptor = ArgumentCaptor.forClass(QueryWrapper.class);
        Mockito.verify(pluginItemService).getOne(itemQueryCaptor.capture());
        Assert.assertTrue(itemQueryCaptor.getValue().toSQL().toUpperCase(Locale.ROOT).contains("FOR UPDATE"));
    }

    /**
     * 验证 MCP 绑定使用锁定读校验最新启用状态。
     *
     * @throws Exception 反射调用失败
     */
    @Test
    public void mcpBindingShouldValidateWithForUpdate() {
        Mcp mcp = new Mcp();
        mcp.setStatus(true);
        mcp.setTenantId(BigInteger.ONE);
        McpService mcpService = Mockito.mock(McpService.class);
        Mockito.when(mcpService.getOne(Mockito.any(QueryWrapper.class))).thenReturn(mcp);
        AgentDependencyAccessService service = createService(
                Mockito.mock(WorkflowService.class),
                Mockito.mock(PluginItemService.class),
                Mockito.mock(PluginMapper.class),
                Mockito.mock(PluginVisibilityService.class),
                mcpService,
                Mockito.mock(DocumentCollectionService.class),
                Mockito.mock(ResourceAccessService.class)
        );

        service.requireMcp(agent(), BigInteger.valueOf(4001));

        ArgumentCaptor<QueryWrapper> queryCaptor = ArgumentCaptor.forClass(QueryWrapper.class);
        Mockito.verify(mcpService).getOne(queryCaptor.capture());
        Assert.assertTrue(queryCaptor.getValue().toSQL().toUpperCase(Locale.ROOT).contains("FOR UPDATE"));
    }

    /**
     * 创建依赖资源校验服务。
     *
     * @param workflowService 工作流服务
     * @param pluginItemService 插件工具服务
     * @param pluginMapper 插件 Mapper
     * @param pluginVisibilityService 插件可见性服务
     * @param mcpService MCP 服务
     * @param documentCollectionService 知识库服务
     * @param resourceAccessService 资源权限服务
     * @return 依赖资源校验服务
     */
    private AgentDependencyAccessService createService(
            WorkflowService workflowService,
            PluginItemService pluginItemService,
            PluginMapper pluginMapper,
            PluginVisibilityService pluginVisibilityService,
            McpService mcpService,
            DocumentCollectionService documentCollectionService,
            ResourceAccessService resourceAccessService) {
        return new AgentDependencyAccessService(
                Mockito.mock(ModelService.class),
                workflowService,
                pluginItemService,
                pluginMapper,
                pluginVisibilityService,
                mcpService,
                documentCollectionService,
                Mockito.mock(AgentCategoryService.class),
                Mockito.mock(CategoryPermissionService.class),
                resourceAccessService
        );
    }

    /**
     * 创建同租户 Agent。
     *
     * @return Agent
     */
    private Agent agent() {
        Agent agent = new Agent();
        agent.setTenantId(BigInteger.ONE);
        return agent;
    }

    /**
     * 创建启用的插件工具。
     *
     * @param pluginId 插件 ID
     * @return 插件工具
     */
    private PluginItem pluginItem(BigInteger pluginId) {
        PluginItem pluginItem = new PluginItem();
        pluginItem.setPluginId(pluginId);
        pluginItem.setStatus(1);
        return pluginItem;
    }
}
