import { NodeSchemaProvider } from './node-schema-provider.js';
export class WorkflowValidator {
    provider;
    constructor(customIndexPath) {
        this.provider = new NodeSchemaProvider(customIndexPath);
    }
    /**
     * Validate a workflow JSON
     */
    validateWorkflow(workflow) {
        const errors = [];
        const warnings = [];
        // 1. Check basic structure
        if (!workflow) {
            errors.push({ type: 'error', message: 'Workflow is null or undefined' });
            return { valid: false, errors, warnings };
        }
        if (typeof workflow !== 'object') {
            errors.push({ type: 'error', message: 'Workflow must be a JSON object' });
            return { valid: false, errors, warnings };
        }
        // 2. Check required fields
        if (!workflow.nodes || !Array.isArray(workflow.nodes)) {
            errors.push({ type: 'error', message: 'Workflow must have a "nodes" array' });
        }
        if (!workflow.connections || typeof workflow.connections !== 'object') {
            errors.push({ type: 'error', message: 'Workflow must have a "connections" object' });
        }
        if (errors.length > 0) {
            return { valid: false, errors, warnings };
        }
        // 3. Validate each node
        const nodeMap = new Map();
        for (const node of workflow.nodes) {
            // Store node for connection validation
            nodeMap.set(node.name, node);
            // Check required node fields
            if (!node.id) {
                errors.push({
                    type: 'error',
                    nodeName: node.name || 'unknown',
                    message: 'Node is missing required field: "id"',
                });
            }
            if (!node.name) {
                errors.push({
                    type: 'error',
                    nodeId: node.id,
                    message: 'Node is missing required field: "name"',
                });
            }
            if (!node.type) {
                errors.push({
                    type: 'error',
                    nodeId: node.id,
                    nodeName: node.name,
                    message: 'Node is missing required field: "type"',
                });
                continue; // Can't validate further without type
            }
            // Extract node name from type (e.g., "n8n-nodes-base.httpRequest" -> "httpRequest")
            const nodeTypeName = node.type.split('.').pop();
            // Check if node type exists
            const nodeSchema = this.provider.getNodeSchema(nodeTypeName);
            if (!nodeSchema) {
                errors.push({
                    type: 'error',
                    nodeId: node.id,
                    nodeName: node.name,
                    message: `Unknown node type: "${node.type}". Use "npx @n8n-as-code/agent-cli search" to find correct node names.`,
                });
                continue;
            }
            // Check typeVersion
            if (node.typeVersion === undefined) {
                warnings.push({
                    type: 'warning',
                    nodeId: node.id,
                    nodeName: node.name,
                    message: 'Node is missing "typeVersion" field',
                });
            }
            // Check position
            if (!node.position || !Array.isArray(node.position) || node.position.length !== 2) {
                warnings.push({
                    type: 'warning',
                    nodeId: node.id,
                    nodeName: node.name,
                    message: 'Node should have "position" as [x, y] array',
                });
            }
            // Check parameters
            if (!node.parameters) {
                warnings.push({
                    type: 'warning',
                    nodeId: node.id,
                    nodeName: node.name,
                    message: 'Node is missing "parameters" object',
                });
            }
            // Validate parameters against schema
            if (node.parameters && nodeSchema.properties) {
                this.validateNodeParameters(node, nodeSchema, errors, warnings);
            }
        }
        // 4. Validate connections
        if (workflow.connections) {
            this.validateConnections(workflow.connections, nodeMap, errors, warnings);
        }
        return {
            valid: errors.length === 0,
            errors,
            warnings,
        };
    }
    /**
     * Validate node parameters against schema
     */
    validateNodeParameters(node, nodeSchema, errors, warnings) {
        const schemaProps = nodeSchema.properties || [];
        const requiredProps = schemaProps.filter((p) => p.required === true);
        // Check required parameters
        for (const prop of requiredProps) {
            if (!(prop.name in node.parameters)) {
                errors.push({
                    type: 'error',
                    nodeId: node.id,
                    nodeName: node.name,
                    message: `Missing required parameter: "${prop.name}"`,
                    path: `nodes[${node.name}].parameters.${prop.name}`,
                });
            }
        }
        // Check for unknown parameters (might be typos)
        const knownParamNames = new Set(schemaProps.map((p) => p.name));
        for (const paramName of Object.keys(node.parameters)) {
            if (!knownParamNames.has(paramName)) {
                warnings.push({
                    type: 'warning',
                    nodeId: node.id,
                    nodeName: node.name,
                    message: `Unknown parameter: "${paramName}". This might be a typo or deprecated parameter.`,
                    path: `nodes[${node.name}].parameters.${paramName}`,
                });
            }
        }
    }
    /**
     * Validate connections between nodes
     */
    validateConnections(connections, nodeMap, errors, warnings) {
        for (const [sourceName, sourceConnections] of Object.entries(connections)) {
            // Check if source node exists
            if (!nodeMap.has(sourceName)) {
                errors.push({
                    type: 'error',
                    message: `Connection references non-existent source node: "${sourceName}"`,
                });
                continue;
            }
            if (typeof sourceConnections !== 'object' || sourceConnections === null) {
                errors.push({
                    type: 'error',
                    nodeName: sourceName,
                    message: `Invalid connections format for node "${sourceName}"`,
                });
                continue;
            }
            // Check main connections
            const mainConnections = sourceConnections.main;
            if (mainConnections && Array.isArray(mainConnections)) {
                for (let outputIndex = 0; outputIndex < mainConnections.length; outputIndex++) {
                    const outputConnections = mainConnections[outputIndex];
                    if (Array.isArray(outputConnections)) {
                        for (const conn of outputConnections) {
                            // Check connection structure
                            if (!conn.node) {
                                errors.push({
                                    type: 'error',
                                    nodeName: sourceName,
                                    message: `Connection missing "node" field`,
                                });
                                continue;
                            }
                            // Check if target node exists
                            if (!nodeMap.has(conn.node)) {
                                errors.push({
                                    type: 'error',
                                    nodeName: sourceName,
                                    message: `Connection references non-existent target node: "${conn.node}"`,
                                });
                            }
                            // Check connection type
                            if (conn.type && conn.type !== 'main') {
                                warnings.push({
                                    type: 'warning',
                                    nodeName: sourceName,
                                    message: `Unusual connection type: "${conn.type}" (expected "main")`,
                                });
                            }
                            // Check index
                            if (conn.index === undefined) {
                                warnings.push({
                                    type: 'warning',
                                    nodeName: sourceName,
                                    message: `Connection to "${conn.node}" missing "index" field`,
                                });
                            }
                        }
                    }
                }
            }
        }
    }
}
//# sourceMappingURL=workflow-validator.js.map