import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { WorkflowValidator } from '@/services/workflow-validator';
import { NodeRepository } from '@/database/node-repository';
import { EnhancedConfigValidator } from '@/services/enhanced-config-validator';
import { ExpressionValidator } from '@/services/expression-validator';
import { createWorkflow } from '@tests/utils/builders/workflow.builder';
import type { WorkflowNode, Workflow } from '@/types/n8n-api';

// Mock dependencies
vi.mock('@/database/node-repository');
vi.mock('@/services/enhanced-config-validator');
vi.mock('@/services/expression-validator');
vi.mock('@/utils/logger');

describe('WorkflowValidator - Comprehensive Tests', () => {
  let validator: WorkflowValidator;
  let mockNodeRepository: NodeRepository;
  let mockEnhancedConfigValidator: typeof EnhancedConfigValidator;

  beforeEach(() => {
    vi.clearAllMocks();

    // Create mock instances
    mockNodeRepository = new NodeRepository({} as any) as any;
    mockEnhancedConfigValidator = EnhancedConfigValidator as any;

    // Ensure the mock repository has all necessary methods
    if (!mockNodeRepository.getAllNodes) {
      mockNodeRepository.getAllNodes = vi.fn();
    }
    if (!mockNodeRepository.getNode) {
      mockNodeRepository.getNode = vi.fn();
    }

    // Mock common node types data
    const nodeTypes: Record<string, any> = {
      'nodes-base.webhook': {
        type: 'nodes-base.webhook',
        displayName: 'Webhook',
        package: 'n8n-nodes-base',
        version: 2,
        isVersioned: true,
        properties: [],
        category: 'trigger'
      },
      'nodes-base.httpRequest': {
        type: 'nodes-base.httpRequest',
        displayName: 'HTTP Request',
        package: 'n8n-nodes-base',
        version: 4,
        isVersioned: true,
        properties: [],
        category: 'network'
      },
      'nodes-base.set': {
        type: 'nodes-base.set',
        displayName: 'Set',
        package: 'n8n-nodes-base',
        version: 3,
        isVersioned: true,
        properties: [],
        category: 'data'
      },
      'nodes-base.code': {
        type: 'nodes-base.code',
        displayName: 'Code',
        package: 'n8n-nodes-base',
        version: 2,
        isVersioned: true,
        properties: [],
        category: 'code'
      },
      'nodes-base.manualTrigger': {
        type: 'nodes-base.manualTrigger',
        displayName: 'Manual Trigger',
        package: 'n8n-nodes-base',
        version: 1,
        isVersioned: true,
        properties: [],
        category: 'trigger'
      },
      'nodes-base.if': {
        type: 'nodes-base.if',
        displayName: 'IF',
        package: 'n8n-nodes-base',
        version: 2,
        isVersioned: true,
        properties: [],
        category: 'logic'
      },
      'nodes-base.slack': {
        type: 'nodes-base.slack',
        displayName: 'Slack',
        package: 'n8n-nodes-base',
        version: 2,
        isVersioned: true,
        properties: [],
        category: 'communication'
      },
      'nodes-base.googleSheets': {
        type: 'nodes-base.googleSheets',
        displayName: 'Google Sheets',
        package: 'n8n-nodes-base',
        version: 4,
        isVersioned: true,
        properties: [],
        category: 'data'
      },
      'nodes-langchain.agent': {
        type: 'nodes-langchain.agent',
        displayName: 'AI Agent',
        package: '@n8n/n8n-nodes-langchain',
        version: 1,
        isVersioned: true,
        properties: [],
        isAITool: true,
        category: 'ai'
      },
      'nodes-base.postgres': {
        type: 'nodes-base.postgres',
        displayName: 'Postgres',
        package: 'n8n-nodes-base',
        version: 2,
        isVersioned: true,
        properties: [],
        category: 'database'
      },
      'community.customNode': {
        type: 'community.customNode',
        displayName: 'Custom Node',
        package: 'n8n-nodes-custom',
        version: 1,
        isVersioned: false,
        properties: [],
        isAITool: false,
        category: 'custom'
      }
    };

    // Set up default mock behaviors
    vi.mocked(mockNodeRepository.getNode).mockImplementation((nodeType: string) => {
      // Handle normalization for custom nodes
      if (nodeType === 'n8n-nodes-custom.customNode') {
        return {
          type: 'n8n-nodes-custom.customNode',
          displayName: 'Custom Node',
          package: 'n8n-nodes-custom',
          version: 1,
          isVersioned: false,
          properties: [],
          isAITool: false
        };
      }

      return nodeTypes[nodeType] || null;
    });

    // Mock getAllNodes for NodeSimilarityService
    vi.mocked(mockNodeRepository.getAllNodes).mockReturnValue(Object.values(nodeTypes));

    vi.mocked(mockEnhancedConfigValidator.validateWithMode).mockReturnValue({
      errors: [],
      warnings: [],
      suggestions: [],
      mode: 'operation' as const,
      valid: true,
      visibleProperties: [],
      hiddenProperties: []
    } as any);

    vi.mocked(ExpressionValidator.validateNodeExpressions).mockReturnValue({
      valid: true,
      errors: [],
      warnings: [],
      usedVariables: new Set(),
      usedNodes: new Set()
    });

    // Create validator instance
    validator = new WorkflowValidator(mockNodeRepository, mockEnhancedConfigValidator);
  });

  describe('validateWorkflow', () => {
    it('should validate a minimal valid workflow', async () => {
      const workflow = createWorkflow('Test Workflow')
        .addWebhookNode({ name: 'Webhook' })
        .build();

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(true);
      expect(result.errors).toHaveLength(0);
      expect(result.statistics.totalNodes).toBe(1);
      expect(result.statistics.enabledNodes).toBe(1);
      expect(result.statistics.triggerNodes).toBe(1);
    });

    it('should validate a workflow with all options disabled', async () => {
      const workflow = createWorkflow('Test Workflow')
        .addWebhookNode({ name: 'Webhook' })
        .build();

      const result = await validator.validateWorkflow(workflow as any, {
        validateNodes: false,
        validateConnections: false,
        validateExpressions: false
      });

      expect(result.valid).toBe(true);
      expect(mockNodeRepository.getNode).not.toHaveBeenCalled();
      expect(ExpressionValidator.validateNodeExpressions).not.toHaveBeenCalled();
    });

    it('should handle validation errors gracefully', async () => {
      const workflow = createWorkflow('Test Workflow')
        .addWebhookNode({ name: 'Webhook' })
        .build();

      // Make the validation throw an error
      vi.mocked(mockNodeRepository.getNode).mockImplementation(() => {
        throw new Error('Database error');
      });

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(false);
      expect(result.errors.length).toBeGreaterThan(0);
      expect(result.errors.some(e => e.message.includes('Database error'))).toBe(true);
    });

    it('should use different validation profiles', async () => {
      const workflow = createWorkflow('Test Workflow')
        .addWebhookNode({ name: 'Webhook' })
        .build();

      const profiles = ['minimal', 'runtime', 'ai-friendly', 'strict'] as const;

      for (const profile of profiles) {
        const result = await validator.validateWorkflow(workflow as any, { profile });
        expect(result).toBeDefined();
        expect(mockEnhancedConfigValidator.validateWithMode).toHaveBeenCalledWith(
          expect.any(String),
          expect.any(Object),
          expect.any(Array),
          'operation',
          profile
        );
      }
    });
  });

  describe('validateWorkflowStructure', () => {
    it('should error when nodes array is missing', async () => {
      const workflow = { connections: {} } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(false);
      expect(result.errors.some(e => e.message === 'Workflow must have a nodes array')).toBe(true);
    });

    it('should error when connections object is missing', async () => {
      const workflow = { nodes: [] } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(false);
      expect(result.errors.some(e => e.message === 'Workflow must have a connections object')).toBe(true);
    });

    it('should warn when workflow has no nodes', async () => {
      const workflow = { nodes: [], connections: {} } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(true); // Empty workflows are valid but get a warning
      expect(result.warnings).toHaveLength(1);
      expect(result.warnings[0].message).toBe('Workflow is empty - no nodes defined');
    });

    it('should error for single non-webhook node workflow', async () => {
      const workflow = {
        nodes: [{
          id: '1',
          name: 'Set',
          type: 'n8n-nodes-base.set',
          position: [100, 100],
          parameters: {}
        }],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(false);
      expect(result.errors.some(e => e.message.includes('Single-node workflows are only valid for webhook endpoints'))).toBe(true);
    });

    it('should warn for webhook without connections', async () => {
      const workflow = {
        nodes: [{
          id: '1',
          name: 'Webhook',
          type: 'n8n-nodes-base.webhook',
          position: [100, 100],
          parameters: {},
          typeVersion: 2
        }],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(true);
      expect(result.warnings.some(w => w.message.includes('Webhook node has no connections'))).toBe(true);
    });

    it('should error for multi-node workflow without connections', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(false);
      expect(result.errors.some(e => e.message.includes('Multi-node workflow has no connections'))).toBe(true);
    });

    it('should detect duplicate node names', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Duplicate node name: "Webhook"'))).toBe(true);
    });

    it('should detect duplicate node IDs', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook1',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '1',
            name: 'Webhook2',
            type: 'n8n-nodes-base.webhook',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Duplicate node ID: "1"'))).toBe(true);
    });

    it('should count trigger nodes correctly', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Schedule',
            type: 'n8n-nodes-base.scheduleTrigger',
            position: [100, 300],
            parameters: {}
          },
          {
            id: '3',
            name: 'Manual',
            type: 'n8n-nodes-base.manualTrigger',
            position: [100, 500],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.statistics.triggerNodes).toBe(3);
    });

    it('should warn when no trigger nodes exist', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Code',
            type: 'n8n-nodes-base.code',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {
          'Set': {
            main: [[{ node: 'Code', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Workflow has no trigger nodes'))).toBe(true);
    });

    it('should not count disabled nodes in enabledNodes count', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {},
            disabled: true
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.statistics.totalNodes).toBe(2);
      expect(result.statistics.enabledNodes).toBe(1);
    });
  });

  describe('validateAllNodes', () => {
    it('should skip disabled nodes', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {},
            disabled: true
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(mockNodeRepository.getNode).not.toHaveBeenCalled();
    });

    it('should accept both nodes-base and n8n-nodes-base prefixes as valid', async () => {
      // This test verifies the fix for false positives - both prefixes are valid
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'nodes-base.webhook', // This is now valid (normalized internally)
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      // Mock the normalized node lookup
      (mockNodeRepository.getNode as any) = vi.fn((type: string) => {
        if (type === 'nodes-base.webhook') {
          return {
            nodeType: 'nodes-base.webhook',
            displayName: 'Webhook',
            properties: [],
            isVersioned: false
          };
        }
        return null;
      });

      const result = await validator.validateWorkflow(workflow as any);

      // Should NOT error for nodes-base prefix - it's valid!
      expect(result.valid).toBe(true);
      expect(result.errors.some(e => e.message.includes('Invalid node type'))).toBe(false);
    });

    it.skip('should handle unknown node types with suggestions', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'httpRequest', // Missing package prefix
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(false);
      expect(result.errors.some(e => e.message.includes('Unknown node type: "httpRequest"'))).toBe(true);
      expect(result.errors.some(e => e.message.includes('Did you mean "n8n-nodes-base.httpRequest"?'))).toBe(true);
    });

    it('should try normalized types for n8n-nodes-base', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(mockNodeRepository.getNode).toHaveBeenCalledWith('nodes-base.webhook');
    });

    it('should skip node repository lookup for langchain nodes', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Agent',
            type: '@n8n/n8n-nodes-langchain.agent',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      // Langchain nodes should skip node repository validation
      // They are validated by dedicated AI validators instead
      expect(mockNodeRepository.getNode).not.toHaveBeenCalledWith('nodes-langchain.agent');
    });

    it('should validate typeVersion for versioned nodes', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
            // Missing typeVersion
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Missing required property \'typeVersion\''))).toBe(true);
    });

    it('should error for invalid typeVersion', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {},
            typeVersion: 'invalid' as any
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Invalid typeVersion: invalid'))).toBe(true);
    });

    it('should warn for outdated typeVersion', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {},
            typeVersion: 1 // Current version is 2
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Outdated typeVersion: 1. Latest is 2'))).toBe(true);
    });

    it('should error for typeVersion exceeding maximum', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {},
            typeVersion: 10 // Max is 2
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('typeVersion 10 exceeds maximum supported version 2'))).toBe(true);
    });

    it('should add node validation errors and warnings', async () => {
      vi.mocked(mockEnhancedConfigValidator.validateWithMode).mockReturnValue({
        errors: [{ type: 'missing_required', property: 'url', message: 'Missing required field: url' }],
        warnings: [{ type: 'security', property: 'url', message: 'Consider using HTTPS' }],
        suggestions: [],
        mode: 'operation' as const,
        valid: false,
        visibleProperties: [],
        hiddenProperties: []
      } as any);

      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            typeVersion: 4
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Missing required field: url'))).toBe(true);
      expect(result.warnings.some(w => w.message.includes('Consider using HTTPS'))).toBe(true);
    });

    it('should handle node validation failures gracefully', async () => {
      vi.mocked(mockEnhancedConfigValidator.validateWithMode).mockImplementation(() => {
        throw new Error('Validation error');
      });

      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            typeVersion: 4
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Failed to validate node: Validation error'))).toBe(true);
    });
  });

  describe('validateConnections', () => {
    it('should validate valid connections', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {
          'Webhook': {
            main: [[{ node: 'Set', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.statistics.validConnections).toBe(1);
      expect(result.statistics.invalidConnections).toBe(0);
    });

    it('should error for connection from non-existent node', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {
          'NonExistent': {
            main: [[{ node: 'Webhook', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Connection from non-existent node: "NonExistent"'))).toBe(true);
      expect(result.statistics.invalidConnections).toBe(1);
    });

    it('should error when using node ID instead of name in source', async () => {
      const workflow = {
        nodes: [
          {
            id: 'webhook-id',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: 'set-id',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {
          'webhook-id': { // Using ID instead of name
            main: [[{ node: 'Set', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Connection uses node ID \'webhook-id\' instead of node name \'Webhook\''))).toBe(true);
    });

    it('should error for connection to non-existent node', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {
          'Webhook': {
            main: [[{ node: 'NonExistent', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Connection to non-existent node: "NonExistent"'))).toBe(true);
      expect(result.statistics.invalidConnections).toBe(1);
    });

    it('should error when using node ID instead of name in target', async () => {
      const workflow = {
        nodes: [
          {
            id: 'webhook-id',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: 'set-id',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {
          'Webhook': {
            main: [[{ node: 'set-id', type: 'main', index: 0 }]] // Using ID instead of name
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Connection target uses node ID \'set-id\' instead of node name \'Set\''))).toBe(true);
    });

    it('should warn for connection to disabled node', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {},
            disabled: true
          }
        ],
        connections: {
          'Webhook': {
            main: [[{ node: 'Set', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Connection to disabled node: "Set"'))).toBe(true);
    });

    it('should validate error outputs', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Error Handler',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {
          'HTTP': {
            error: [[{ node: 'Error Handler', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.statistics.validConnections).toBe(1);
    });

    it('should validate AI tool connections', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Agent',
            type: '@n8n/n8n-nodes-langchain.agent',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Tool',
            type: 'n8n-nodes-base.httpRequest',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {
          'Agent': {
            ai_tool: [[{ node: 'Tool', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.statistics.validConnections).toBe(1);
    });

    it('should warn for community nodes used as AI tools', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Agent',
            type: '@n8n/n8n-nodes-langchain.agent',
            position: [100, 100],
            parameters: {},
            typeVersion: 1
          },
          {
            id: '2',
            name: 'CustomTool',
            type: 'n8n-nodes-custom.customNode',
            position: [300, 100],
            parameters: {},
            typeVersion: 1
          }
        ],
        connections: {
          'Agent': {
            ai_tool: [[{ node: 'CustomTool', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Community node "CustomTool" is being used as an AI tool'))).toBe(true);
    });

    it('should warn for orphaned nodes', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          },
          {
            id: '3',
            name: 'Orphaned',
            type: 'n8n-nodes-base.code',
            position: [500, 100],
            parameters: {}
          }
        ],
        connections: {
          'Webhook': {
            main: [[{ node: 'Set', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Node is not connected to any other nodes') && w.nodeName === 'Orphaned')).toBe(true);
    });

    it('should detect cycles in workflow', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Node1',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Node2',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          },
          {
            id: '3',
            name: 'Node3',
            type: 'n8n-nodes-base.set',
            position: [500, 100],
            parameters: {}
          }
        ],
        connections: {
          'Node1': {
            main: [[{ node: 'Node2', type: 'main', index: 0 }]]
          },
          'Node2': {
            main: [[{ node: 'Node3', type: 'main', index: 0 }]]
          },
          'Node3': {
            main: [[{ node: 'Node1', type: 'main', index: 0 }]] // Creates cycle
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Workflow contains a cycle'))).toBe(true);
    });

    it('should handle null connections properly', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'IF',
            type: 'n8n-nodes-base.if',
            position: [100, 100],
            parameters: {},
            typeVersion: 2
          },
          {
            id: '2',
            name: 'True Branch',
            type: 'n8n-nodes-base.set',
            position: [300, 50],
            parameters: {},
            typeVersion: 3
          }
        ],
        connections: {
          'IF': {
            main: [
              [{ node: 'True Branch', type: 'main', index: 0 }],
              null // False branch not connected
            ]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.statistics.validConnections).toBe(1);
      expect(result.valid).toBe(true);
    });
  });

  describe('validateExpressions', () => {
    it('should validate expressions in node parameters', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {
              values: {
                string: [
                  {
                    name: 'field',
                    value: '={{ $json.data }}'
                  }
                ]
              }
            }
          }
        ],
        connections: {
          'Webhook': {
            main: [[{ node: 'Set', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(ExpressionValidator.validateNodeExpressions).toHaveBeenCalledWith(
        expect.objectContaining({ values: expect.any(Object) }),
        expect.objectContaining({
          availableNodes: expect.arrayContaining(['Webhook']),
          currentNodeName: 'Set',
          hasInputData: true
        })
      );
    });

    it('should add expression errors to result', async () => {
      vi.mocked(ExpressionValidator.validateNodeExpressions).mockReturnValue({
        valid: false,
        errors: ['Invalid expression syntax'],
        warnings: ['Deprecated variable usage'],
        usedVariables: new Set(['$json']),
        usedNodes: new Set()
      });

      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {
              value: '={{ invalid }}'
            }
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Expression error: Invalid expression syntax'))).toBe(true);
      expect(result.warnings.some(w => w.message.includes('Expression warning: Deprecated variable usage'))).toBe(true);
      expect(result.statistics.expressionsValidated).toBe(1);
    });

    it('should skip expression validation for disabled nodes', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {
              value: '={{ $json.data }}'
            },
            disabled: true
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(ExpressionValidator.validateNodeExpressions).not.toHaveBeenCalled();
    });
  });

  describe('checkWorkflowPatterns', () => {
    it('should suggest error handling for large workflows', async () => {
      const builder = createWorkflow('Large Workflow');
      
      // Add more than 3 nodes
      for (let i = 0; i < 5; i++) {
        builder.addCustomNode('n8n-nodes-base.set', 3, {}, { name: `Set${i}` });
      }

      const workflow = builder.build() as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Consider adding error handling'))).toBe(true);
    });

    it('should warn about long linear chains', async () => {
      const builder = createWorkflow('Linear Workflow');
      
      // Create a chain of 12 nodes
      const nodeNames: string[] = [];
      for (let i = 0; i < 12; i++) {
        const nodeName = `Node${i}`;
        builder.addCustomNode('n8n-nodes-base.set', 3, {}, { name: nodeName });
        nodeNames.push(nodeName);
      }

      // Connect them sequentially
      builder.connectSequentially(nodeNames);

      const workflow = builder.build() as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Long linear chain detected'))).toBe(true);
    });

    it('should warn about missing credentials', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Slack',
            type: 'n8n-nodes-base.slack',
            position: [100, 100],
            parameters: {},
            credentials: {
              slackApi: {} // Missing id
            }
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Missing credentials configuration for slackApi'))).toBe(true);
    });

    it('should warn about AI agents without tools', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Agent',
            type: '@n8n/n8n-nodes-langchain.agent',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('AI Agent has no tools connected'))).toBe(true);
    });

    it('should suggest community package setting for AI tools', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Agent',
            type: '@n8n/n8n-nodes-langchain.agent',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Tool',
            type: 'n8n-nodes-base.httpRequest',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {
          'Agent': {
            ai_tool: [[{ node: 'Tool', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE'))).toBe(true);
    });
  });

  describe('checkNodeErrorHandling', () => {
    it('should error when node-level properties are inside parameters', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            typeVersion: 4,
            parameters: {
              url: 'https://api.example.com',
              onError: 'continueRegularOutput', // Wrong location!
              retryOnFail: true, // Wrong location!
              credentials: {} // Wrong location!
            }
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Node-level properties onError, retryOnFail, credentials are in the wrong location'))).toBe(true);
      expect(result.errors.some(e => e.details?.fix?.includes('Move these properties from node.parameters to the node level'))).toBe(true);
    });

    it('should validate onError property values', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            onError: 'invalidValue' as any
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Invalid onError value: "invalidValue"'))).toBe(true);
    });

    it('should warn about deprecated continueOnFail', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            continueOnFail: true
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Using deprecated "continueOnFail: true"'))).toBe(true);
    });

    it('should error for conflicting error handling properties', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            continueOnFail: true,
            onError: 'continueRegularOutput'
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('Cannot use both "continueOnFail" and "onError" properties'))).toBe(true);
    });

    it('should validate retry configuration', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            retryOnFail: true,
            maxTries: 'invalid' as any,
            waitBetweenTries: -1000
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.errors.some(e => e.message.includes('maxTries must be a positive number'))).toBe(true);
      expect(result.errors.some(e => e.message.includes('waitBetweenTries must be a non-negative number'))).toBe(true);
    });

    it('should warn about excessive retry values', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            retryOnFail: true,
            maxTries: 15,
            waitBetweenTries: 400000
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('maxTries is set to 15'))).toBe(true);
      expect(result.warnings.some(w => w.message.includes('waitBetweenTries is set to 400000ms'))).toBe(true);
    });

    it('should warn about retryOnFail without maxTries', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            retryOnFail: true
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('retryOnFail is enabled but maxTries is not specified'))).toBe(true);
    });

    it('should validate other node-level properties', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {},
            typeVersion: 3,
            alwaysOutputData: 'invalid' as any,
            executeOnce: 'invalid' as any,
            disabled: 'invalid' as any,
            notesInFlow: 'invalid' as any,
            notes: 123 as any
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);


      expect(result.errors.some(e => e.message.includes('alwaysOutputData must be a boolean'))).toBe(true);
      expect(result.errors.some(e => e.message.includes('executeOnce must be a boolean'))).toBe(true);
      expect(result.errors.some(e => e.message.includes('disabled must be a boolean'))).toBe(true);
      expect(result.errors.some(e => e.message.includes('notesInFlow must be a boolean'))).toBe(true);
      expect(result.errors.some(e => e.message.includes('notes must be a string'))).toBe(true);
    });

    it('should warn about executeOnce', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {},
            executeOnce: true
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('executeOnce is enabled'))).toBe(true);
    });

    it('should warn error-prone nodes without error handling', async () => {
      const errorProneNodes = [
        { type: 'n8n-nodes-base.httpRequest', message: 'HTTP Request', version: 4 },
        { type: 'n8n-nodes-base.webhook', message: 'Webhook', version: 2 },
        { type: 'n8n-nodes-base.postgres', message: 'Database operation', version: 2 },
        { type: 'n8n-nodes-base.slack', message: 'slack node', version: 2 }
      ];

      for (const nodeInfo of errorProneNodes) {
        const workflow = {
          nodes: [
            {
              id: '1',
              name: 'Node',
              type: nodeInfo.type,
              position: [100, 100],
              parameters: {},
              typeVersion: nodeInfo.version
            }
          ],
          connections: {}
        } as any;

        const result = await validator.validateWorkflow(workflow as any);

        expect(result.warnings.some(w => w.message.includes(nodeInfo.message) && w.message.includes('without error handling'))).toBe(true);
      }
    });

    it('should warn about conflicting error handling', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            continueOnFail: true,
            retryOnFail: true
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.warnings.some(w => w.message.includes('Both continueOnFail and retryOnFail are enabled'))).toBe(true);
    });

    it('should suggest alwaysOutputData for debugging', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            retryOnFail: true
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('Consider enabling alwaysOutputData'))).toBe(true);
    });

    it('should provide general error handling suggestions', async () => {
      const builder = createWorkflow('No Error Handling');
      
      // Add 6 nodes without error handling
      for (let i = 0; i < 6; i++) {
        builder.addCustomNode('n8n-nodes-base.httpRequest', 4, {}, { name: `HTTP${i}` });
      }

      const workflow = builder.build() as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('Most nodes lack error handling'))).toBe(true);
    });

    it('should suggest replacing deprecated error handling', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [100, 100],
            parameters: {},
            continueOnFail: true
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('Replace "continueOnFail: true" with "onError:'))).toBe(true);
    });
  });

  describe('generateSuggestions', () => {
    it('should suggest adding trigger for workflows without triggers', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('Add a trigger node'))).toBe(true);
    });

    it('should provide connection examples for connection errors', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {} // Missing connections
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('Example connection structure'))).toBe(true);
      expect(result.suggestions.some(s => s.includes('Use node NAMES (not IDs) in connections'))).toBe(true);
    });

    it('should suggest error handling when missing', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {}
          },
          {
            id: '2',
            name: 'HTTP',
            type: 'n8n-nodes-base.httpRequest',
            position: [300, 100],
            parameters: {}
          }
        ],
        connections: {
          'Webhook': {
            main: [[{ node: 'HTTP', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('Add error handling'))).toBe(true);
    });

    it('should suggest breaking up large workflows', async () => {
      const builder = createWorkflow('Large Workflow');
      
      // Add 25 nodes
      for (let i = 0; i < 25; i++) {
        builder.addCustomNode('n8n-nodes-base.set', 3, {}, { name: `Node${i}` });
      }

      const workflow = builder.build() as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('Consider breaking this workflow into smaller sub-workflows'))).toBe(true);
    });

    it('should suggest Code node for complex expressions', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Complex',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {
              field1: '={{ $json.a }}',
              field2: '={{ $json.b }}',
              field3: '={{ $json.c }}',
              field4: '={{ $json.d }}',
              field5: '={{ $json.e }}',
              field6: '={{ $json.f }}'
            }
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('Consider using a Code node for complex data transformations'))).toBe(true);
    });

    it('should suggest minimal workflow structure', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.suggestions.some(s => s.includes('A minimal workflow needs'))).toBe(true);
    });
  });

  describe('findSimilarNodeTypes', () => {
    it.skip('should find similar node types for common mistakes', async () => {
      // Test that webhook without prefix gets suggestions
      const webhookWorkflow = {
        nodes: [
          {
            id: '1',
            name: 'Node',
            type: 'webhook',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const webhookResult = await validator.validateWorkflow(webhookWorkflow);

      // Check that we get an unknown node error with suggestions
      const unknownNodeError = webhookResult.errors.find(e =>
        e.message && e.message.includes('Unknown node type')
      );
      expect(unknownNodeError).toBeDefined();

      // For webhook, it should definitely suggest nodes-base.webhook
      expect(unknownNodeError?.message).toContain('nodes-base.webhook');

      // Test that slack without prefix gets suggestions
      const slackWorkflow = {
        nodes: [
          {
            id: '1',
            name: 'Node',
            type: 'slack',
            position: [100, 100],
            parameters: {}
          }
        ],
        connections: {}
      } as any;

      const slackResult = await validator.validateWorkflow(slackWorkflow);
      const slackError = slackResult.errors.find(e =>
        e.message && e.message.includes('Unknown node type')
      );
      expect(slackError).toBeDefined();
      expect(slackError?.message).toContain('nodes-base.slack');
    });
  });

  describe('Integration Tests', () => {
    it('should validate a complex workflow with multiple issues', async () => {
      const workflow = {
        nodes: [
          // Valid trigger
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            position: [100, 100],
            parameters: {},
            typeVersion: 2
          },
          // Node with valid alternative prefix (no longer an error)
          {
            id: '2',
            name: 'HTTP1',
            type: 'nodes-base.httpRequest', // Valid prefix (normalized internally)
            position: [300, 100],
            parameters: {}
          },
          // Node with missing typeVersion
          {
            id: '3',
            name: 'Slack',
            type: 'n8n-nodes-base.slack',
            position: [500, 100],
            parameters: {}
          },
          // Disabled node
          {
            id: '4',
            name: 'Disabled',
            type: 'n8n-nodes-base.set',
            position: [700, 100],
            parameters: {},
            disabled: true
          },
          // Node with error handling in wrong place
          {
            id: '5',
            name: 'HTTP2',
            type: 'n8n-nodes-base.httpRequest',
            position: [900, 100],
            parameters: {
              onError: 'continueRegularOutput'
            },
            typeVersion: 4
          },
          // Orphaned node
          {
            id: '6',
            name: 'Orphaned',
            type: 'n8n-nodes-base.code',
            position: [1100, 100],
            parameters: {},
            typeVersion: 2
          },
          // AI Agent without tools
          {
            id: '7',
            name: 'Agent',
            type: '@n8n/n8n-nodes-langchain.agent',
            position: [100, 300],
            parameters: {},
            typeVersion: 1
          }
        ],
        connections: {
          'Webhook': {
            main: [[{ node: 'HTTP1', type: 'main', index: 0 }]]
          },
          'HTTP1': {
            main: [[{ node: 'Slack', type: 'main', index: 0 }]]
          },
          'Slack': {
            main: [[{ node: 'Disabled', type: 'main', index: 0 }]]
          },
          // Using ID instead of name
          '5': {
            main: [[{ node: 'Agent', type: 'main', index: 0 }]]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      // Should have multiple errors (but not for the nodes-base prefix)
      expect(result.valid).toBe(false);
      expect(result.errors.length).toBeGreaterThan(2); // Reduced by 1 since nodes-base prefix is now valid

      // Specific errors (removed the invalid node type error as it's no longer invalid)
      expect(result.errors.some(e => e.message.includes('Missing required property \'typeVersion\''))).toBe(true);
      expect(result.errors.some(e => e.message.includes('Node-level properties onError are in the wrong location'))).toBe(true);
      expect(result.errors.some(e => e.message.includes('Connection uses node ID \'5\' instead of node name'))).toBe(true);

      // Warnings
      expect(result.warnings.some(w => w.message.includes('Connection to disabled node'))).toBe(true);
      expect(result.warnings.some(w => w.message.includes('Node is not connected') && w.nodeName === 'Orphaned')).toBe(true);
      expect(result.warnings.some(w => w.message.includes('AI Agent has no tools connected'))).toBe(true);

      // Statistics
      expect(result.statistics.totalNodes).toBe(7);
      expect(result.statistics.enabledNodes).toBe(6);
      expect(result.statistics.triggerNodes).toBe(1);
      expect(result.statistics.invalidConnections).toBeGreaterThan(0);

      // Suggestions
      expect(result.suggestions.length).toBeGreaterThan(0);
    });

    it('should validate a perfect workflow', async () => {
      const workflow = {
        nodes: [
          {
            id: '1',
            name: 'Manual Trigger',
            type: 'n8n-nodes-base.manualTrigger',
            position: [250, 300],
            parameters: {},
            typeVersion: 1
          },
          {
            id: '2',
            name: 'HTTP Request',
            type: 'n8n-nodes-base.httpRequest',
            position: [450, 300],
            parameters: {
              url: 'https://api.example.com/data',
              method: 'GET'
            },
            typeVersion: 4,
            onError: 'continueErrorOutput',
            retryOnFail: true,
            maxTries: 3,
            waitBetweenTries: 1000
          },
          {
            id: '3',
            name: 'Process Data',
            type: 'n8n-nodes-base.code',
            position: [650, 300],
            parameters: {
              jsCode: 'return items;'
            },
            typeVersion: 2
          },
          {
            id: '4',
            name: 'Error Handler',
            type: 'n8n-nodes-base.set',
            position: [650, 500],
            parameters: {
              values: {
                string: [
                  {
                    name: 'error',
                    value: 'An error occurred'
                  }
                ]
              }
            },
            typeVersion: 3
          }
        ],
        connections: {
          'Manual Trigger': {
            main: [[{ node: 'HTTP Request', type: 'main', index: 0 }]]
          },
          'HTTP Request': {
            main: [
              [{ node: 'Process Data', type: 'main', index: 0 }],
              [{ node: 'Error Handler', type: 'main', index: 0 }]
            ]
          }
        }
      } as any;

      const result = await validator.validateWorkflow(workflow as any);

      expect(result.valid).toBe(true);
      expect(result.errors).toHaveLength(0);
      expect(result.warnings).toHaveLength(0);
      expect(result.statistics.validConnections).toBe(3);
      expect(result.statistics.invalidConnections).toBe(0);
    });
  });
});