package tech.easyflow.admin.service.ai;

import com.mybatisflex.core.query.QueryWrapper;
import com.easyagents.flow.core.parser.ChainParser;
import org.mockito.MockedStatic;
import org.testng.Assert;
import org.testng.annotations.Test;
import tech.easyflow.ai.easyagentsflow.service.WorkflowDatacenterContentService;
import tech.easyflow.ai.entity.Model;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.plugin.workflow.snapshot.WorkflowPluginSnapshotResolver;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.ai.service.ModelService;
import tech.easyflow.ai.service.PluginItemService;
import tech.easyflow.ai.service.PluginService;
import tech.easyflow.ai.service.PluginVisibilityService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.service.WorkflowUsageAuthorizationService;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.datacenter.execution.service.DatacenterDatasetQueryService;
import tech.easyflow.datacenter.meta.entity.DatacenterSource;
import tech.easyflow.datacenter.meta.service.DatacenterDatasetRegistryService;
import tech.easyflow.datacenter.meta.service.DatacenterSourceService;
import tech.easyflow.system.service.ResourceAccessService;

import java.math.BigInteger;
import java.util.List;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

/**
 * {@link WorkflowDesignerOptionService} 工作流引用权限测试。
 */
public class WorkflowDesignerOptionServiceTest {

    /**
     * 验证客户端提交候选列表之外的模型 ID 时服务端拒绝保存。
     */
    @Test
    public void shouldRejectModelOutsideSelectableOptions() {
        ModelService modelService = mock(ModelService.class);
        DocumentCollectionService knowledgeService = mock(DocumentCollectionService.class);
        when(modelService.listByIds(any())).thenReturn(List.of());
        when(knowledgeService.list(any(QueryWrapper.class))).thenReturn(List.of());
        WorkflowDesignerOptionService service = createService(
                modelService, knowledgeService, mock(DatacenterSourceService.class));

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount).thenReturn(loginAccount());

            BusinessException exception = Assert.expectThrows(
                    BusinessException.class,
                    () -> service.assertContentReferences(
                            "{\"nodes\":[{\"type\":\"llmNode\",\"data\":{\"llmId\":\"99\"}}]}")
            );

            Assert.assertTrue(exception.getMessage().contains("模型"));
        }
    }

    /**
     * 验证工作流数据节点不能引用其他租户的数据源。
     */
    @Test
    public void shouldRejectCrossTenantDataSource() {
        ModelService modelService = mock(ModelService.class);
        DocumentCollectionService knowledgeService = mock(DocumentCollectionService.class);
        DatacenterSourceService sourceService = mock(DatacenterSourceService.class);
        when(modelService.listSelectableModels(any(Model.class), eq(false), eq("id"), eq("desc")))
                .thenReturn(List.of());
        when(knowledgeService.list(any(QueryWrapper.class))).thenReturn(List.of());
        DatacenterSource source = new DatacenterSource();
        source.setId(BigInteger.valueOf(9));
        source.setTenantId(BigInteger.valueOf(200));
        when(sourceService.getById(BigInteger.valueOf(9))).thenReturn(source);
        WorkflowDesignerOptionService service = createService(
                modelService, knowledgeService, sourceService);

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount).thenReturn(loginAccount());

            BusinessException exception = Assert.expectThrows(
                    BusinessException.class,
                    () -> service.assertContentReferences("""
                            {"nodes":[{"type":"search-dataset-node","data":{
                              "datasetRef":{"sourceId":"9"}
                            }}]}
                            """)
            );

            Assert.assertTrue(exception.getMessage().contains("数据源"));
        }
    }

    /**
     * 验证子流程节点配置拒绝读取其他租户的工作流。
     */
    @Test
    public void shouldRejectCrossTenantChildWorkflow() {
        WorkflowService workflowService = mock(WorkflowService.class);
        Workflow workflow = new Workflow();
        workflow.setId(BigInteger.valueOf(19));
        workflow.setTenantId(BigInteger.valueOf(200));
        when(workflowService.getById(BigInteger.valueOf(19))).thenReturn(workflow);
        WorkflowDesignerOptionService service = createService(
                mock(ModelService.class),
                mock(DocumentCollectionService.class),
                mock(DatacenterSourceService.class),
                workflowService
        );

        try (MockedStatic<SaTokenUtil> saToken = mockStatic(SaTokenUtil.class)) {
            saToken.when(SaTokenUtil::getLoginAccount).thenReturn(loginAccount());

            BusinessException exception = Assert.expectThrows(
                    BusinessException.class,
                    () -> service.getChildWorkflowNodeData(
                            BigInteger.valueOf(10),
                            BigInteger.valueOf(19))
            );

            Assert.assertTrue(exception.getMessage().contains("子流程"));
        }
    }

    private WorkflowDesignerOptionService createService(
            ModelService modelService,
            DocumentCollectionService knowledgeService,
            DatacenterSourceService sourceService) {
        return createService(modelService, knowledgeService, sourceService, mock(WorkflowService.class));
    }

    private WorkflowDesignerOptionService createService(
            ModelService modelService,
            DocumentCollectionService knowledgeService,
            DatacenterSourceService sourceService,
            WorkflowService workflowService) {
        ResourceAccessService resourceAccessService = mock(ResourceAccessService.class);
        return new WorkflowDesignerOptionService(
                modelService,
                knowledgeService,
                mock(PluginService.class),
                mock(PluginItemService.class),
                mock(PluginVisibilityService.class),
                workflowService,
                new WorkflowUsageAuthorizationService(workflowService, resourceAccessService),
                mock(WorkflowPluginSnapshotResolver.class),
                mock(ChainParser.class),
                mock(WorkflowDatacenterContentService.class),
                resourceAccessService,
                sourceService,
                mock(DatacenterDatasetRegistryService.class),
                mock(DatacenterDatasetQueryService.class)
        );
    }

    private LoginAccount loginAccount() {
        LoginAccount account = new LoginAccount();
        account.setId(BigInteger.ONE);
        account.setTenantId(BigInteger.valueOf(100));
        return account;
    }
}
