package tech.easyflow.admin.service.ai;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.easyagents.flow.core.chain.ChainDefinition;
import com.easyagents.flow.core.chain.Node;
import com.easyagents.flow.core.node.ConfirmNode;
import com.easyagents.flow.core.node.EndNode;
import com.easyagents.flow.core.node.StartNode;
import com.easyagents.flow.core.parser.ChainParser;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Service;
import tech.easyflow.admin.model.ai.WorkflowDesignerOptionsView;
import tech.easyflow.ai.easyagentsflow.service.WorkflowDatacenterContentService;
import tech.easyflow.ai.entity.DocumentCollection;
import tech.easyflow.ai.entity.Model;
import tech.easyflow.ai.entity.ModelProvider;
import tech.easyflow.ai.entity.Plugin;
import tech.easyflow.ai.entity.PluginItem;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.enums.PluginType;
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.entity.DatacenterTable;
import tech.easyflow.datacenter.entity.DatacenterTableField;
import tech.easyflow.datacenter.execution.model.DatacenterSchemaResponse;
import tech.easyflow.datacenter.execution.model.DatasetRef;
import tech.easyflow.datacenter.execution.service.DatacenterDatasetQueryService;
import tech.easyflow.datacenter.meta.entity.DatacenterSource;
import tech.easyflow.datacenter.meta.enums.DatacenterSourceType;
import tech.easyflow.datacenter.meta.model.DatacenterCatalogMeta;
import tech.easyflow.datacenter.meta.service.DatacenterDatasetRegistryService;
import tech.easyflow.datacenter.meta.service.DatacenterSourceService;
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.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 查询工作流设计器所需的场景化安全选项。
 */
@Service
public class WorkflowDesignerOptionService {

    private final ModelService modelService;
    private final DocumentCollectionService documentCollectionService;
    private final PluginService pluginService;
    private final PluginItemService pluginItemService;
    private final PluginVisibilityService pluginVisibilityService;
    private final WorkflowService workflowService;
    private final WorkflowUsageAuthorizationService workflowUsageAuthorizationService;
    private final WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver;
    private final ChainParser chainParser;
    private final WorkflowDatacenterContentService workflowDatacenterContentService;
    private final ResourceAccessService resourceAccessService;
    private final DatacenterSourceService datacenterSourceService;
    private final DatacenterDatasetRegistryService datacenterDatasetRegistryService;
    private final DatacenterDatasetQueryService datacenterDatasetQueryService;

    /**
     * 创建工作流设计器选项服务。
     *
     * @param modelService 模型服务
     * @param documentCollectionService 知识库服务
     * @param pluginService 插件服务
     * @param pluginItemService 插件工具服务
     * @param pluginVisibilityService 插件可见性服务
     * @param workflowService 工作流服务
     * @param workflowUsageAuthorizationService 工作流使用权限校验服务
     * @param workflowPluginSnapshotResolver 工作流插件快照解析器
     * @param chainParser 工作流解析器
     * @param workflowDatacenterContentService 工作流数据中心内容服务
     * @param resourceAccessService 资源访问服务
     * @param datacenterSourceService 数据源服务
     * @param datacenterDatasetRegistryService 数据集注册服务
     * @param datacenterDatasetQueryService 数据集查询服务
     */
    public WorkflowDesignerOptionService(
            ModelService modelService,
            DocumentCollectionService documentCollectionService,
            PluginService pluginService,
            PluginItemService pluginItemService,
            PluginVisibilityService pluginVisibilityService,
            WorkflowService workflowService,
            WorkflowUsageAuthorizationService workflowUsageAuthorizationService,
            WorkflowPluginSnapshotResolver workflowPluginSnapshotResolver,
            ChainParser chainParser,
            WorkflowDatacenterContentService workflowDatacenterContentService,
            ResourceAccessService resourceAccessService,
            DatacenterSourceService datacenterSourceService,
            DatacenterDatasetRegistryService datacenterDatasetRegistryService,
            DatacenterDatasetQueryService datacenterDatasetQueryService) {
        this.modelService = modelService;
        this.documentCollectionService = documentCollectionService;
        this.pluginService = pluginService;
        this.pluginItemService = pluginItemService;
        this.pluginVisibilityService = pluginVisibilityService;
        this.workflowService = workflowService;
        this.workflowUsageAuthorizationService = workflowUsageAuthorizationService;
        this.workflowPluginSnapshotResolver = workflowPluginSnapshotResolver;
        this.chainParser = chainParser;
        this.workflowDatacenterContentService = workflowDatacenterContentService;
        this.resourceAccessService = resourceAccessService;
        this.datacenterSourceService = datacenterSourceService;
        this.datacenterDatasetRegistryService = datacenterDatasetRegistryService;
        this.datacenterDatasetQueryService = datacenterDatasetQueryService;
    }

    /**
     * 查询设计器初始化所需的模型和知识库。
     *
     * @param codeEngines 代码执行引擎选项
     * @return 设计器初始化选项
     */
    public WorkflowDesignerOptionsView listOptions(List<Map<String, Object>> codeEngines) {
        LoginAccount account = requireAccount();
        return new WorkflowDesignerOptionsView(
                listModelOptions(account),
                listKnowledgeOptions(account),
                codeEngines == null ? List.of() : codeEngines
        );
    }

    /**
     * 校验工作流内容引用的场景资源是否仍允许当前账号使用。
     *
     * @param content 工作流内容
     * @throws BusinessException 内容引用越权、跨租户或资源失效时抛出
     */
    public void assertContentReferences(String content) {
        if (content == null || content.isBlank()) {
            return;
        }
        JSONObject root;
        try {
            root = JSON.parseObject(content);
        } catch (Exception exception) {
            throw new BusinessException("工作流内容不是合法JSON");
        }
        JSONArray nodes = root.getJSONArray("nodes");
        if (nodes == null || nodes.isEmpty()) {
            return;
        }
        LoginAccount account = requireAccount();
        Set<BigInteger> modelIds = new HashSet<>();
        Set<BigInteger> knowledgeIds = new HashSet<>();
        Set<BigInteger> checkedPluginItemIds = new HashSet<>();
        Set<BigInteger> checkedWorkflowIds = new HashSet<>();
        Set<BigInteger> checkedSourceIds = new HashSet<>();
        Set<String> checkedTableIds = new HashSet<>();

        for (int index = 0; index < nodes.size(); index++) {
            JSONObject node = nodes.getJSONObject(index);
            if (node == null) {
                continue;
            }
            JSONObject data = node.getJSONObject("data");
            if (data == null) {
                continue;
            }
            String nodeType = data.getString("type");
            if (nodeType == null || nodeType.isBlank()) {
                nodeType = node.getString("type");
            }
            if ("llmNode".equals(nodeType)) {
                addReferenceId(modelIds, readReferenceId(data, "llmId", "模型"));
            } else if ("knowledgeNode".equals(nodeType)) {
                addReferenceId(knowledgeIds, readReferenceId(data, "knowledgeId", "知识库"));
            } else if ("plugin-node".equals(nodeType)) {
                BigInteger pluginItemId = readReferenceId(data, "pluginId", "插件工具");
                if (pluginItemId != null && checkedPluginItemIds.add(pluginItemId)) {
                    getPluginTinyFlowData(pluginItemId);
                }
            } else if ("workflow-node".equals(nodeType)) {
                assertWorkflowReference(readReferenceId(data, "workflowId", "子流程"),
                        account, checkedWorkflowIds);
            }
            assertDatasetReference(data, account, checkedSourceIds, checkedTableIds);
        }
        assertModelReferences(modelIds, account);
        assertKnowledgeReferences(knowledgeIds, account);
    }

    /**
     * 分页查询当前账号可用于工作流的插件。
     *
     * @param pageNumber 页码
     * @param pageSize 每页数量
     * @param keyword 插件名称或描述关键字
     * @return 插件安全选项分页
     */
    public Page<WorkflowDesignerOptionsView.PluginOption> pagePlugins(
            Long pageNumber, Long pageSize, String keyword) {
        LoginAccount account = requireAccount();
        QueryWrapper wrapper = QueryWrapper.create()
                .eq(Plugin::getTenantId, account.getTenantId().longValue())
                .orderBy(Plugin::getCreated, false);
        List<Plugin> plugins = pluginService.getMapper().selectListWithRelationsByQuery(wrapper);
        String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase(java.util.Locale.ROOT);
        List<Plugin> availablePlugins = pluginService.preparePluginsForCurrentUser(plugins, false, true).stream()
                .filter(plugin -> matchesKeyword(normalizedKeyword, plugin.getName(), plugin.getDescription()))
                .toList();
        List<WorkflowDesignerOptionsView.PluginOption> options = availablePlugins.stream()
                .map(this::toPluginOption)
                .toList();
        long actualPageNumber = pageNumber == null || pageNumber < 1 ? 1L : pageNumber;
        long actualPageSize = pageSize == null || pageSize < 1 ? 10L : Math.min(pageSize, 100L);
        int fromIndex = Math.toIntExact(Math.min(options.size(), (actualPageNumber - 1) * actualPageSize));
        int toIndex = Math.toIntExact(Math.min(options.size(), fromIndex + actualPageSize));
        return new Page<>(
                options.subList(fromIndex, toIndex),
                actualPageNumber,
                actualPageSize,
                options.size()
        );
    }

    /**
     * 判断任一候选文本是否包含关键字。
     *
     * @param keyword 已归一化的小写关键字
     * @param values 候选文本
     * @return 空关键字或任一文本命中时返回 {@code true}
     */
    private boolean matchesKeyword(String keyword, String... values) {
        if (keyword == null || keyword.isEmpty()) {
            return true;
        }
        for (String value : values) {
            if (value != null && value.toLowerCase(java.util.Locale.ROOT).contains(keyword)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 查询一个插件工具的工作流节点安全配置。
     *
     * @param pluginItemId 插件工具 ID
     * @return 节点配置
     * @throws BusinessException 插件工具不存在或不可用时抛出
     */
    public JSONObject getPluginTinyFlowData(BigInteger pluginItemId) {
        if (pluginItemId == null) {
            throw new BusinessException("插件工具不能为空");
        }
        PluginItem record = pluginItemService.getById(pluginItemId);
        if (record == null || !Integer.valueOf(1).equals(record.getStatus())) {
            throw new BusinessException("插件工具不存在或已禁用");
        }
        Plugin plugin = pluginService.getById(record.getPluginId());
        if (plugin == null) {
            throw new BusinessException("插件不存在");
        }
        LoginAccount account = requireAccount();
        if (!Objects.equals(plugin.getTenantId(), account.getTenantId().longValue())) {
            throw new BusinessException("无权限访问插件");
        }
        pluginVisibilityService.assertPluginVisible(plugin.getCreatedBy(), plugin.getId(), "无权限访问插件");
        Plugin preparedPlugin = pluginService.preparePluginForCurrentUser(plugin);
        if (Boolean.FALSE.equals(preparedPlugin.getAvailable())) {
            throw new BusinessException(preparedPlugin.getReasonMessage());
        }

        JSONObject nodeData = new JSONObject();
        nodeData.put("pluginId", record.getId().toString());
        nodeData.put("pluginName", record.getName());
        nodeData.put("pluginType", preparedPlugin.getType());
        nodeData.put("workflowId", preparedPlugin.getWorkflowId());
        nodeData.put("workflowTitle", preparedPlugin.getWorkflowTitle());
        nodeData.put("available", preparedPlugin.getAvailable());
        nodeData.put("reasonCode", preparedPlugin.getReasonCode());
        nodeData.put("reasonMessage", preparedPlugin.getReasonMessage());
        nodeData.put("parameters", parseSchemaArray(record.getInputData()));
        nodeData.put("outputDefs", parseSchemaArray(record.getOutputData()));
        nodeData.put("schemaHash", resolveSchemaHash(record, preparedPlugin));
        return nodeData;
    }

    /**
     * 查询子流程节点所需的输入输出定义。
     *
     * @param currentWorkflowId 当前工作流 ID
     * @param childWorkflowId 子流程 ID
     * @return 子流程节点配置
     * @throws BusinessException 子流程不存在、不可用或无权使用时抛出
     */
    public JSONObject getChildWorkflowNodeData(
            BigInteger currentWorkflowId,
            BigInteger childWorkflowId) {
        if (childWorkflowId == null) {
            throw new BusinessException("子流程不能为空");
        }
        if (Objects.equals(childWorkflowId, currentWorkflowId)) {
            throw new BusinessException("工作流不能作为自身子节点");
        }
        LoginAccount account = requireAccount();
        Workflow workflow = workflowUsageAuthorizationService.requireUsableWorkflow(
                childWorkflowId,
                account,
                "子流程不存在、已禁用或无权使用");
        assertContentReferences(workflow.getContent());

        ChainDefinition definition = chainParser.parse(
                workflowDatacenterContentService.prepareContent(workflow.getContent()));
        JSONArray inputs = new JSONArray();
        JSONArray outputs = new JSONArray();
        for (Node node : definition.getNodes()) {
            if (node instanceof StartNode) {
                inputs = JSON.parseArray(JSON.toJSONString(node.getParameters()));
                decorateChildWorkflowSchemaArray(inputs);
            }
            if (node instanceof EndNode endNode) {
                outputs = JSON.parseArray(JSON.toJSONString(endNode.getOutputDefs()));
                decorateChildWorkflowSchemaArray(outputs);
            }
            if (node instanceof ConfirmNode) {
                throw new BusinessException("工作流存在【确认节点】，暂不支持作为子节点");
            }
        }

        JSONObject nodeData = new JSONObject();
        nodeData.put("workflowId", workflow.getId());
        nodeData.put("workflowName", workflow.getTitle());
        nodeData.put("parameters", inputs);
        nodeData.put("outputDefs", outputs);
        return nodeData;
    }

    /**
     * 查询当前租户可用于工作流的数据源安全选项。
     *
     * @return 数据源安全选项
     */
    public List<WorkflowDesignerOptionsView.DataSourceOption> listDataSources() {
        LoginAccount account = requireAccount();
        datacenterDatasetRegistryService.ensureBuiltinSource(DatacenterSourceType.PROJECT_MYSQL, account);
        return datacenterSourceService.list(QueryWrapper.create()
                        .eq(DatacenterSource::getTenantId, account.getTenantId())
                        .orderBy(DatacenterSource::getModified, false))
                .stream()
                .map(source -> new WorkflowDesignerOptionsView.DataSourceOption(
                        source.getId(), source.getSourceName(), source.getSourceType()))
                .toList();
    }

    /**
     * 查询一个数据源的目录安全选项。
     *
     * @param sourceId 数据源 ID
     * @return 目录安全选项
     */
    public List<WorkflowDesignerOptionsView.CatalogOption> listCatalogs(BigInteger sourceId) {
        LoginAccount account = requireAccount();
        requireTenantSource(sourceId, account);
        return datacenterSourceService.listCatalogs(sourceId, account).stream()
                .map(this::toCatalogOption)
                .toList();
    }

    /**
     * 查询一个数据源目录下已接入的数据集。
     *
     * @param sourceId 数据源 ID
     * @param catalogId 目录 ID
     * @return 数据集安全选项
     */
    public List<WorkflowDesignerOptionsView.DatasetOption> listManagedDatasets(
            BigInteger sourceId,
            BigInteger catalogId) {
        LoginAccount account = requireAccount();
        requireTenantSource(sourceId, account);
        return datacenterDatasetRegistryService.listManagedTables(sourceId, catalogId).stream()
                .filter(table -> Objects.equals(table.getTenantId(), account.getTenantId()))
                .filter(table -> Objects.equals(table.getSourceId(), sourceId))
                .filter(table -> catalogId == null || Objects.equals(table.getCatalogId(), catalogId))
                .map(this::toDatasetOption)
                .toList();
    }

    /**
     * 查询工作流数据节点所需的数据集结构。
     *
     * @param datasetRef 数据集引用
     * @return 数据集结构安全视图
     */
    public WorkflowDesignerOptionsView.DatasetSchemaOption getDatasetSchema(DatasetRef datasetRef) {
        if (datasetRef == null || datasetRef.getSourceId() == null || datasetRef.getTableId() == null) {
            throw new BusinessException("数据集引用不完整");
        }
        LoginAccount account = requireAccount();
        requireTenantSource(datasetRef.getSourceId(), account);
        DatacenterTable table = datacenterDatasetRegistryService.getTableWithFields(datasetRef.getTableId());
        if (table == null
                || !Objects.equals(table.getTenantId(), account.getTenantId())
                || !Objects.equals(table.getSourceId(), datasetRef.getSourceId())) {
            throw new BusinessException("数据集不存在或无权访问");
        }
        DatacenterSchemaResponse schema = datacenterDatasetQueryService.getSchema(datasetRef);
        List<DatacenterTableField> fields = schema == null || schema.getFields() == null
                ? Collections.emptyList()
                : schema.getFields();
        return new WorkflowDesignerOptionsView.DatasetSchemaOption(
                table.getTableName(),
                table.getTableDesc(),
                fields.stream()
                        .map(field -> new WorkflowDesignerOptionsView.DatasetFieldOption(
                                field.getFieldName(),
                                field.getFieldDesc(),
                                field.getJdbcType(),
                                field.getFieldType()
                        ))
                        .toList()
        );
    }

    private List<WorkflowDesignerOptionsView.ModelOption> listModelOptions(LoginAccount account) {
        Model query = new Model();
        query.setTenantId(account.getTenantId());
        query.setModelType(Model.MODEL_TYPES[0]);
        return modelService.listSelectableModels(query, false, "id", "desc").stream()
                .filter(model -> Objects.equals(model.getTenantId(), account.getTenantId()))
                .map(model -> {
                    ModelProvider provider = model.getModelProvider();
                    WorkflowDesignerOptionsView.ProviderOption providerOption = provider == null
                            ? null
                            : new WorkflowDesignerOptionsView.ProviderOption(
                            provider.getProviderName(),
                            provider.getProviderType(),
                            provider.getIcon()
                    );
                    return new WorkflowDesignerOptionsView.ModelOption(
                            model.getId(),
                            model.getTitle(),
                            model.getDescription(),
                            providerOption
                    );
                })
                .toList();
    }

    private List<WorkflowDesignerOptionsView.KnowledgeOption> listKnowledgeOptions(LoginAccount account) {
        return documentCollectionService.list(QueryWrapper.create()
                        .eq(DocumentCollection::getTenantId, account.getTenantId())
                        .orderBy(DocumentCollection::getModified, false))
                .stream()
                .filter(item -> resourceAccessService.canAccess(
                        account, CategoryResourceType.KNOWLEDGE, item, ResourceAction.USE))
                .map(item -> new WorkflowDesignerOptionsView.KnowledgeOption(
                        item.getId(), item.getTitle(), item.getDescription()))
                .toList();
    }

    private void addReferenceId(Set<BigInteger> resourceIds, BigInteger resourceId) {
        if (resourceId != null) {
            resourceIds.add(resourceId);
        }
    }

    private void assertModelReferences(Set<BigInteger> modelIds, LoginAccount account) {
        if (modelIds.isEmpty()) {
            return;
        }
        List<Model> models = modelService.listByIds(modelIds);
        boolean valid = models.size() == modelIds.size()
                && models.stream().allMatch(model ->
                modelIds.contains(model.getId())
                        && Objects.equals(model.getTenantId(), account.getTenantId())
                        && Model.MODEL_TYPES[0].equals(model.getModelType()));
        if (!valid) {
            throw new BusinessException("模型不存在、已失效或无权使用");
        }
    }

    private void assertKnowledgeReferences(Set<BigInteger> knowledgeIds, LoginAccount account) {
        if (knowledgeIds.isEmpty()) {
            return;
        }
        List<DocumentCollection> knowledges = documentCollectionService.listByIds(knowledgeIds);
        boolean valid = knowledges.size() == knowledgeIds.size()
                && knowledges.stream().allMatch(knowledge ->
                knowledgeIds.contains(knowledge.getId())
                        && Objects.equals(knowledge.getTenantId(), account.getTenantId())
                        && resourceAccessService.canAccess(
                        account, CategoryResourceType.KNOWLEDGE, knowledge, ResourceAction.USE));
        if (!valid) {
            throw new BusinessException("知识库不存在、已失效或无权使用");
        }
    }

    private void assertWorkflowReference(
            BigInteger workflowId,
            LoginAccount account,
            Set<BigInteger> checkedWorkflowIds) {
        if (workflowId == null || !checkedWorkflowIds.add(workflowId)) {
            return;
        }
        workflowUsageAuthorizationService.requireUsableWorkflow(
                workflowId,
                account,
                "子流程不存在、已禁用或无权使用");
    }

    private void assertDatasetReference(
            JSONObject data,
            LoginAccount account,
            Set<BigInteger> checkedSourceIds,
            Set<String> checkedTableIds) {
        JSONObject datasetRef = data.getJSONObject("datasetRef");
        if (datasetRef == null) {
            return;
        }
        BigInteger sourceId = readReferenceId(datasetRef, "sourceId", "数据源");
        if (sourceId != null && checkedSourceIds.add(sourceId)) {
            requireTenantSource(sourceId, account);
        }
        BigInteger tableId = readReferenceId(datasetRef, "tableId", "数据集");
        if (tableId == null) {
            return;
        }
        if (sourceId == null) {
            throw new BusinessException("数据集缺少数据源引用");
        }
        String checkedKey = sourceId + ":" + tableId;
        if (!checkedTableIds.add(checkedKey)) {
            return;
        }
        DatacenterTable table = datacenterDatasetRegistryService.getTableWithFields(tableId);
        if (table == null
                || !Objects.equals(table.getTenantId(), account.getTenantId())
                || !Objects.equals(table.getSourceId(), sourceId)) {
            throw new BusinessException("数据集不存在或无权使用");
        }
    }

    private BigInteger readReferenceId(JSONObject data, String key, String resourceName) {
        Object value = data.get(key);
        if (value == null || String.valueOf(value).isBlank()) {
            return null;
        }
        try {
            return new BigInteger(String.valueOf(value));
        } catch (NumberFormatException exception) {
            throw new BusinessException(resourceName + "引用格式无效");
        }
    }

    private WorkflowDesignerOptionsView.PluginOption toPluginOption(Plugin plugin) {
        List<WorkflowDesignerOptionsView.PluginToolOption> tools =
                plugin.getTools() == null ? List.of() : plugin.getTools().stream()
                        .filter(tool -> Integer.valueOf(1).equals(tool.getStatus()))
                        .map(tool -> new WorkflowDesignerOptionsView.PluginToolOption(
                                tool.getId(), tool.getName(), tool.getDescription()))
                        .toList();
        return new WorkflowDesignerOptionsView.PluginOption(
                plugin.getId(),
                plugin.getName(),
                plugin.getDescription(),
                plugin.getIcon(),
                tools
        );
    }

    private JSONArray parseSchemaArray(String content) {
        if (content == null || content.isBlank()) {
            return new JSONArray();
        }
        JSONArray array = JSON.parseArray(content);
        decorateSchemaArray(array);
        return array;
    }

    private void decorateSchemaArray(JSONArray array) {
        for (Object item : array) {
            if (!(item instanceof JSONObject value)) {
                continue;
            }
            value.put("id", cn.hutool.core.util.IdUtil.simpleUUID());
            value.put("nameDisabled", true);
            value.put("dataTypeDisabled", true);
            value.put("deleteDisabled", true);
            value.put("addChildDisabled", true);
            JSONArray children = value.getJSONArray("children");
            if (children != null) {
                decorateSchemaArray(children);
            }
        }
    }

    /**
     * 为子流程参数补充设计器只读元数据。
     *
     * @param array 子流程参数定义
     */
    private void decorateChildWorkflowSchemaArray(JSONArray array) {
        for (Object item : array) {
            if (!(item instanceof JSONObject value)) {
                continue;
            }
            value.put("id", cn.hutool.core.util.IdUtil.simpleUUID());
            value.put("nameDisabled", true);
            value.put("dataTypeDisabled", true);
            value.put("deleteDisabled", true);
            value.put("addChildDisabled", true);
            value.put("refType", "ref");
            JSONArray children = value.getJSONArray("children");
            if (children != null) {
                decorateChildWorkflowSchemaArray(children);
            }
        }
    }

    private String resolveSchemaHash(PluginItem record, Plugin plugin) {
        if (record.getSchemaHash() != null && !record.getSchemaHash().isBlank()) {
            return record.getSchemaHash();
        }
        if (!PluginType.isWorkflow(plugin.getType()) || plugin.getWorkflowId() == null) {
            return null;
        }
        Workflow workflow = workflowService.getPublishedById(plugin.getWorkflowId());
        return workflow == null ? null : workflowPluginSnapshotResolver.resolveSchemaHash(workflow);
    }

    private WorkflowDesignerOptionsView.CatalogOption toCatalogOption(DatacenterCatalogMeta catalog) {
        return new WorkflowDesignerOptionsView.CatalogOption(
                catalog.getId(),
                catalog.getSourceId(),
                catalog.getCatalogName(),
                catalog.getCatalogDesc()
        );
    }

    private WorkflowDesignerOptionsView.DatasetOption toDatasetOption(DatacenterTable table) {
        return new WorkflowDesignerOptionsView.DatasetOption(
                table.getId(),
                table.getSourceId(),
                table.getCatalogId(),
                table.getTableName(),
                table.getTableDesc()
        );
    }

    private DatacenterSource requireTenantSource(BigInteger sourceId, LoginAccount account) {
        if (sourceId == null) {
            throw new BusinessException("数据源不能为空");
        }
        DatacenterSource source = datacenterSourceService.getById(sourceId);
        if (source == null || !Objects.equals(source.getTenantId(), account.getTenantId())) {
            throw new BusinessException("数据源不存在或无权访问");
        }
        return source;
    }

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