import type { Mock, MockedClass, MockedFunction } from 'vitest';
import { vi } from 'vitest';
import { TriggerSuggestionEngine } from '../trigger-suggestion-engine.js';
import { ProcessStore } from '../process-store.js';
import { 
  ProcessDefinition,
  Activity,
  PersonaType,
  TriggerSuggestion
} from '../types.js';

// Mock the store
vi.mock('../process-store.js');

describe('TriggerSuggestionEngine', () => {
  let engine: TriggerSuggestionEngine;
  let mockStore: vi.Mocked<ProcessStore>;
  
  beforeEach(() => {
    mockStore = {
      getAllProcesses: vi.fn().mockResolvedValue([])
    } as any;
    
    engine = new TriggerSuggestionEngine(mockStore);
  });
  
  afterEach(() => {
    vi.clearAllMocks();
  });
  
  describe('suggestTriggers', () => {
    it('should suggest schedule trigger for report generation', async () => {
      const process = createProcess({
        name: 'Weekly Sales Report',
        activities: [
          createActivity('generate-report', 'Generate Report'),
          createActivity('send-email', 'Send Email')
        ]
      });
      
      const suggestions = await engine.suggestTriggers(process);
      
      expect(suggestions.length).toBeGreaterThan(0);
      expect(suggestions[0].trigger.type).toBe('schedule');
      expect(suggestions[0].confidence).toBeGreaterThan(0.8);
      expect(suggestions[0].reasoning).toContain('Report generation');
    });
    
    it('should suggest continuous monitoring schedule', async () => {
      const process = createProcess({
        name: 'System Health Monitor',
        activities: [
          createActivity('check-status', 'Monitor System Status'),
          createActivity('alert', 'Send Alert if Down')
        ]
      });
      
      const suggestions = await engine.suggestTriggers(process);
      
      expect(suggestions[0].trigger.type).toBe('schedule');
      expect(suggestions[0].trigger.config.cron).toBe('*/15 * * * *'); // Every 15 minutes
      expect(suggestions[0].reasoning).toContain('Continuous monitoring');
    });
    
    it('should suggest review schedule with human interaction', async () => {
      const process = createProcess({
        name: 'Code Review Process',
        activities: [
          createActivity('gather-prs', 'Gather Pull Requests'),
          createHumanActivity('review', 'Review Code'),
          createActivity('update-status', 'Update PR Status')
        ]
      });
      
      const suggestions = await engine.suggestTriggers(process);
      
      expect(suggestions[0].trigger.type).toBe('schedule');
      expect(suggestions[0].trigger.config.cron).toBe('0 9 * * 1-5'); // Weekdays at 9 AM
      expect(suggestions[0].reasoning).toContain('business hours');
    });
    
    it('should use persona-specific patterns', async () => {
      const process = createProcess({
        persona: 'software-engineer',
        name: 'Development Tasks',
        activities: [
          createActivity('check-ci', 'Check CI Status'),
          createActivity('run-tests', 'Run Tests')
        ]
      });
      
      const suggestions = await engine.suggestTriggers(process);
      
      // Should include both schedule and event triggers for engineers
      const scheduleSuggestion = suggestions.find(s => s.trigger.type === 'schedule');
      const eventSuggestion = suggestions.find(s => s.trigger.type === 'event');
      
      expect(scheduleSuggestion).toBeDefined();
      expect(eventSuggestion).toBeDefined();
      expect(scheduleSuggestion?.reasoning).toContain('Daily development tasks');
    });
    
    it('should suggest event trigger for data processing', async () => {
      const process = createProcess({
        name: 'Data ETL Pipeline',
        activities: [
          createActivity('extract', 'Extract Data'),
          createActivity('transform', 'Transform Data'),
          createActivity('load', 'Load to Database')
        ]
      });
      
      const suggestions = await engine.suggestTriggers(process);
      
      // Should include event-based trigger as alternative
      const eventSuggestion = suggestions.find(s => s.trigger.type === 'event');
      expect(eventSuggestion).toBeDefined();
      expect(eventSuggestion?.reasoning).toContain('data changes');
    });
    
    it('should detect scheduling conflicts', async () => {
      // Mock existing process with same schedule
      const existingProcess = createProcess({
        id: 'existing-123',
        name: 'Existing Daily Task',
        triggers: [{
          id: 'trigger-1',
          type: 'schedule',
          name: 'Daily',
          enabled: true,
          config: { cron: '0 9 * * *' }
        }]
      });
      
      mockStore.getAllProcesses.mockResolvedValue([existingProcess]);
      
      const newProcess = createProcess({
        name: 'New Daily Task',
        activities: [createActivity('task', 'Daily Task')]
      });
      
      const suggestions = await engine.suggestTriggers(newProcess);
      
      // Check if conflicts are detected
      const scheduleSuggestion = suggestions.find(s => 
        s.trigger.type === 'schedule' && 
        s.trigger.config.cron === '0 9 * * *'
      );
      
      if (scheduleSuggestion && scheduleSuggestion.conflicts) {
        expect(scheduleSuggestion.conflicts.length).toBeGreaterThan(0);
        expect(scheduleSuggestion.conflicts[0].conflictType).toBe('time');
        expect(scheduleSuggestion.conflicts[0].suggestion).toContain('offset');
      }
    });
    
    it('should determine appropriate schedule based on process name', async () => {
      const testCases = [
        { name: 'Daily Standup Notes', expectedCron: '0 8 * * *', description: 'daily' },
        { name: 'Weekly Team Report', expectedCron: '0 8 * * 1', description: 'weekly' },
        { name: 'Monthly Financial Review', expectedCron: '0 8 1 * *', description: 'monthly' }
      ];
      
      for (const testCase of testCases) {
        const process = createProcess({
          name: testCase.name,
          activities: [createActivity('generate', 'Generate Report')]
        });
        
        const suggestions = await engine.suggestTriggers(process);
        
        expect(suggestions[0].trigger.config.cron).toBe(testCase.expectedCron);
        expect(suggestions[0].reasoning).toContain(testCase.description);
      }
    });
    
    it('should suggest manual trigger as fallback', async () => {
      const process = createProcess({
        name: 'Miscellaneous Task',
        activities: [createActivity('task', 'Do Something')]
      });
      
      const suggestions = await engine.suggestTriggers(process);
      
      // Should have manual trigger when no clear pattern
      const manualSuggestion = suggestions.find(s => s.trigger.type === 'manual');
      expect(manualSuggestion).toBeDefined();
      expect(manualSuggestion?.confidence).toBeLessThan(0.6);
      expect(manualSuggestion?.reasoning).toContain('flexibility');
    });
    
    it('should generate alternatives', async () => {
      const process = createProcess({
        name: 'Data Processing Job',
        activities: [
          createActivity('process', 'Process Data'),
          createExternalActivity('api', 'Call External API')
        ]
      });
      
      const suggestions = await engine.suggestTriggers(process);
      
      // Should have multiple suggestions
      expect(suggestions.length).toBeGreaterThan(1);
      
      // Should include different trigger types
      const triggerTypes = suggestions.map(s => s.trigger.type);
      expect(triggerTypes).toContain('schedule');
      expect(triggerTypes).toContain('event');
    });
    
    it('should sort suggestions by confidence', async () => {
      const process = createProcess({
        name: 'Complex Workflow',
        activities: [
          createActivity('step1', 'Step 1'),
          createActivity('step2', 'Step 2')
        ]
      });
      
      const suggestions = await engine.suggestTriggers(process);
      
      // Verify sorted by confidence (descending)
      for (let i = 1; i < suggestions.length; i++) {
        expect(suggestions[i-1].confidence).toBeGreaterThanOrEqual(suggestions[i].confidence);
      }
    });
  });
});

// Helper functions
function createProcess(overrides: Partial<ProcessDefinition> = {}): ProcessDefinition {
  return {
    id: 'process-test-123',
    name: 'Test Process',
    version: '1.0.0',
    triggers: [],
    activities: [],
    variables: {},
    metadata: {
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
      executionCount: 0
    },
    ...overrides
  };
}

function createActivity(id: string, name: string): Activity {
  return {
    id,
    type: 'tool',
    name,
    config: {
      toolName: 'test_tool',
      toolArgs: {}
    }
  };
}

function createHumanActivity(id: string, name: string): Activity {
  return {
    id,
    type: 'human',
    name,
    config: {
      prompt: 'Please review',
      approvalType: 'any'
    }
  };
}

function createExternalActivity(id: string, name: string): Activity {
  return {
    id,
    type: 'external',
    name,
    config: {
      url: 'https://api.example.com',
      method: 'GET'
    }
  };
}