import { describe, it, expect, vi, beforeEach } from 'vitest';
import { WorkflowValidator } from '@/services/workflow-validator';
import { EnhancedConfigValidator } from '@/services/enhanced-config-validator';

// Mock logger to prevent console output
vi.mock('@/utils/logger', () => ({
  Logger: vi.fn().mockImplementation(() => ({
    error: vi.fn(),
    warn: vi.fn(),
    info: vi.fn()
  }))
}));

describe('WorkflowValidator - Simple Unit Tests', () => {
  let validator: WorkflowValidator;
  
  // Create a simple mock repository
  const createMockRepository = (nodeData: Record<string, any>) => ({
    getNode: vi.fn((type: string) => nodeData[type] || null),
    findSimilarNodes: vi.fn().mockReturnValue([])
  });

  // Create a simple mock validator class
  const createMockValidatorClass = (validationResult: any) => ({
    validateWithMode: vi.fn().mockReturnValue(validationResult)
  });

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

  describe('Basic validation scenarios', () => {
    it('should pass validation for a webhook workflow with single node', async () => {
      // Arrange
      const nodeData = {
        'n8n-nodes-base.webhook': {
          type: 'nodes-base.webhook',
          displayName: 'Webhook',
          name: 'webhook',
          version: 1,
          isVersioned: true,
          properties: []
        },
        'nodes-base.webhook': {
          type: 'nodes-base.webhook',
          displayName: 'Webhook',
          name: 'webhook',
          version: 1,
          isVersioned: true,
          properties: []
        }
      };

      const mockRepository = createMockRepository(nodeData);
      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      const workflow = {
        name: 'Webhook Workflow',
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook',
            typeVersion: 1,
            position: [250, 300] as [number, number],
            parameters: {}
          }
        ],
        connections: {}
      };

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

      // Assert
      expect(result.valid).toBe(true);
      expect(result.errors).toHaveLength(0);
      // Single webhook node should just have a warning about no connections
      expect(result.warnings.some(w => w.message.includes('no connections'))).toBe(true);
    });

    it('should fail validation for unknown node types', async () => {
      // Arrange
      const mockRepository = createMockRepository({}); // Empty node data
      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      const workflow = {
        name: 'Test Workflow',
        nodes: [
          {
            id: '1',
            name: 'Unknown',
            type: 'n8n-nodes-base.unknownNode',
            position: [250, 300] as [number, number],
            parameters: {}
          }
        ],
        connections: {}
      };

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

      // Assert
      expect(result.valid).toBe(false);
      // Check for either the error message or valid being false
      const hasUnknownNodeError = result.errors.some(e =>
        e.message && (e.message.includes('Unknown node type') || e.message.includes('unknown-node-type'))
      );
      expect(result.errors.length > 0 || hasUnknownNodeError).toBe(true);
    });

    it('should detect duplicate node names', async () => {
      // Arrange
      const mockRepository = createMockRepository({});
      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      const workflow = {
        name: 'Duplicate Names',
        nodes: [
          {
            id: '1',
            name: 'HTTP Request',
            type: 'n8n-nodes-base.httpRequest',
            position: [250, 300] as [number, number],
            parameters: {}
          },
          {
            id: '2',
            name: 'HTTP Request', // Duplicate name
            type: 'n8n-nodes-base.httpRequest',
            position: [450, 300] as [number, number],
            parameters: {}
          }
        ],
        connections: {}
      };

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

      // Assert
      expect(result.valid).toBe(false);
      expect(result.errors.some(e => e.message.includes('Duplicate node name'))).toBe(true);
    });

    it('should validate connections properly', async () => {
      // Arrange
      const nodeData = {
        'n8n-nodes-base.manualTrigger': {
          type: 'nodes-base.manualTrigger',
          displayName: 'Manual Trigger',
          isVersioned: false,
          properties: []
        },
        'nodes-base.manualTrigger': {
          type: 'nodes-base.manualTrigger',
          displayName: 'Manual Trigger',
          isVersioned: false,
          properties: []
        },
        'n8n-nodes-base.set': {
          type: 'nodes-base.set',
          displayName: 'Set',
          version: 2,
          isVersioned: true,
          properties: []
        },
        'nodes-base.set': {
          type: 'nodes-base.set',
          displayName: 'Set',
          version: 2,
          isVersioned: true,
          properties: []
        }
      };

      const mockRepository = createMockRepository(nodeData);
      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      const workflow = {
        name: 'Connected Workflow',
        nodes: [
          {
            id: '1',
            name: 'Manual Trigger',
            type: 'n8n-nodes-base.manualTrigger',
            position: [250, 300] as [number, number],
            parameters: {}
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            typeVersion: 2,
            position: [450, 300] as [number, number],
            parameters: {}
          }
        ],
        connections: {
          'Manual Trigger': {
            main: [[{ node: 'Set', type: 'main', index: 0 }]]
          }
        }
      };

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

      // Assert
      expect(result.valid).toBe(true);
      expect(result.statistics.validConnections).toBe(1);
      expect(result.statistics.invalidConnections).toBe(0);
    });

    it('should detect workflow cycles', async () => {
      // Arrange
      const nodeData = {
        'n8n-nodes-base.set': {
          type: 'nodes-base.set',
          displayName: 'Set',
          isVersioned: true,
          version: 2,
          properties: []
        },
        'nodes-base.set': {
          type: 'nodes-base.set',
          displayName: 'Set',
          isVersioned: true,
          version: 2,
          properties: []
        }
      };

      const mockRepository = createMockRepository(nodeData);
      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      const workflow = {
        name: 'Cyclic Workflow',
        nodes: [
          {
            id: '1',
            name: 'Node A',
            type: 'n8n-nodes-base.set',
            typeVersion: 2,
            position: [250, 300] as [number, number],
            parameters: {}
          },
          {
            id: '2',
            name: 'Node B',
            type: 'n8n-nodes-base.set',
            typeVersion: 2,
            position: [450, 300] as [number, number],
            parameters: {}
          }
        ],
        connections: {
          'Node A': {
            main: [[{ node: 'Node B', type: 'main', index: 0 }]]
          },
          'Node B': {
            main: [[{ node: 'Node A', type: 'main', index: 0 }]] // Creates a cycle
          }
        }
      };

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

      // Assert
      expect(result.valid).toBe(false);
      expect(result.errors.some(e => e.message.includes('cycle'))).toBe(true);
    });

    it('should handle null workflow gracefully', async () => {
      // Arrange
      const mockRepository = createMockRepository({});
      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      // Act
      const result = await validator.validateWorkflow(null as any);

      // Assert
      expect(result.valid).toBe(false);
      expect(result.errors[0].message).toContain('workflow is null or undefined');
    });

    it('should require connections for multi-node workflows', async () => {
      // Arrange
      const nodeData = {
        'n8n-nodes-base.manualTrigger': {
          type: 'nodes-base.manualTrigger',
          displayName: 'Manual Trigger',
          properties: []
        },
        'nodes-base.manualTrigger': {
          type: 'nodes-base.manualTrigger',
          displayName: 'Manual Trigger',
          properties: []
        },
        'n8n-nodes-base.set': {
          type: 'nodes-base.set',
          displayName: 'Set',
          version: 2,
          isVersioned: true,
          properties: []
        },
        'nodes-base.set': {
          type: 'nodes-base.set',
          displayName: 'Set',
          version: 2,
          isVersioned: true,
          properties: []
        }
      };

      const mockRepository = createMockRepository(nodeData);
      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      const workflow = {
        name: 'No Connections',
        nodes: [
          {
            id: '1',
            name: 'Manual Trigger',
            type: 'n8n-nodes-base.manualTrigger',
            position: [250, 300] as [number, number],
            parameters: {}
          },
          {
            id: '2',
            name: 'Set',
            type: 'n8n-nodes-base.set',
            typeVersion: 2,
            position: [450, 300] as [number, number],
            parameters: {}
          }
        ],
        connections: {} // No connections between nodes
      };

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

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

    it('should validate typeVersion for versioned nodes', async () => {
      // Arrange
      const nodeData = {
        'n8n-nodes-base.httpRequest': {
          type: 'nodes-base.httpRequest',
          displayName: 'HTTP Request',
          isVersioned: true,
          version: 3, // Latest version is 3
          properties: []
        },
        'nodes-base.httpRequest': {
          type: 'nodes-base.httpRequest',
          displayName: 'HTTP Request',
          isVersioned: true,
          version: 3,
          properties: []
        }
      };

      const mockRepository = createMockRepository(nodeData);
      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      const workflow = {
        name: 'Version Test',
        nodes: [
          {
            id: '1',
            name: 'HTTP Request',
            type: 'n8n-nodes-base.httpRequest',
            typeVersion: 2, // Outdated version
            position: [250, 300] as [number, number],
            parameters: {}
          }
        ],
        connections: {}
      };

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

      // Assert
      expect(result.warnings.some(w => w.message.includes('Outdated typeVersion'))).toBe(true);
    });

    it('should normalize and validate nodes-base prefix to find the node', async () => {
      // Arrange - Test that full-form types are normalized to short form to find the node
      // The repository only has the node under the SHORT normalized key (database format)
      const nodeData = {
        'nodes-base.webhook': {  // Repository has it under SHORT form (database format)
          type: 'nodes-base.webhook',
          displayName: 'Webhook',
          isVersioned: true,
          version: 2,
          properties: []
        }
      };

      // Mock repository that simulates the normalization behavior
      // After our changes, getNode is called with the already-normalized type (short form)
      const mockRepository = {
        getNode: vi.fn((type: string) => {
          // The validator now normalizes to short form before calling getNode
          // So getNode receives 'nodes-base.webhook'
          if (type === 'nodes-base.webhook') {
            return nodeData['nodes-base.webhook'];
          }
          return null;
        }),
        findSimilarNodes: vi.fn().mockReturnValue([])
      };

      const mockValidatorClass = createMockValidatorClass({
        valid: true,
        errors: [],
        warnings: [],
        suggestions: []
      });

      validator = new WorkflowValidator(mockRepository as any, mockValidatorClass as any);

      const workflow = {
        name: 'Valid Alternative Prefix',
        nodes: [
          {
            id: '1',
            name: 'Webhook',
            type: 'n8n-nodes-base.webhook', // Using the full-form prefix (will be normalized to short)
            position: [250, 300] as [number, number],
            parameters: {},
            typeVersion: 2
          }
        ],
        connections: {}
      };

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

      // Assert - The node should be found through normalization
      expect(result.valid).toBe(true);
      expect(result.errors).toHaveLength(0);

      // Verify the repository was called (once with original, once with normalized)
      expect(mockRepository.getNode).toHaveBeenCalled();
    });
  });
});