import { vi } from 'vitest';
import { ActivityExecutor } from '../activity-executor.js';
import { ProcessEngine } from '../process-engine.js';
import { ProcessStore } from '../process-store.js';
import { AgentOrchestrator } from '../../agent-orchestration/orchestrator.js';
import { 
  Activity,
  ProcessDefinition,
  ProcessExecution,
  AgentActivityConfig
} from '../types.js';

describe('Agent Integration Tests', () => {
  let activityExecutor: ActivityExecutor;
  let processEngine: ProcessEngine;
  let processStore: ProcessStore;
  let agentOrchestrator: AgentOrchestrator;
  
  beforeEach(() => {
    // Mock ProcessStore
    processStore = {
      init: vi.fn(),
      saveProcess: vi.fn(),
      getProcess: vi.fn(),
      saveExecution: vi.fn(),
      getExecution: vi.fn(),
      getAllProcesses: vi.fn(),
      getProcessExecutions: vi.fn()
    } as any;
    
    // Create real AgentOrchestrator instance
    agentOrchestrator = new AgentOrchestrator();
    
    // Create real ProcessEngine with real AgentOrchestrator
    processEngine = new ProcessEngine(processStore, agentOrchestrator);
    
    // Extract the ActivityExecutor from ProcessEngine for testing
    activityExecutor = (processEngine as any).activityExecutor;
  });
  
  describe('Agent Activity Execution', () => {
    it('should create agent and execute task through orchestration', async () => {
      const agentActivity: Activity = {
        id: 'agent-test-1',
        type: 'agent',
        name: 'Code Analysis Task',
        config: {
          agentType: 'analyzer',
          agentTask: 'Analyze the codebase for potential improvements',
          agentCapabilities: ['research', 'analysis'],
          priority: 'high'
        }
      };
      
      const inputs = {
        codebase: '/path/to/code',
        focus: 'performance'
      };
      
      const mockExecution: ProcessExecution = {
        id: 'exec-123',
        processId: 'proc-123',
        processVersion: '1.0.0',
        status: 'running',
        triggeredBy: 'manual',
        startedAt: new Date().toISOString(),
        variables: {},
        activityResults: [],
        logs: []
      };
      
      const result = await activityExecutor.execute(agentActivity, inputs, mockExecution);
      
      // Verify agent was created and task executed
      expect(result).toMatchObject({
        agentType: 'analyzer',
        agentId: expect.any(String),
        taskId: expect.any(String),
        taskCompleted: expect.any(Boolean),
        results: expect.any(String), // AgentOrchestrator returns string output
        artifacts: expect.any(Array),
        duration: expect.any(Number)
      });
      
      // Verify agent was created in orchestrator
      const createdAgent = agentOrchestrator.getAgentState(result.agentId);
      expect(createdAgent).toBeDefined();
      expect(createdAgent?.role).toBe('researcher'); // analyzer maps to researcher
    });
    
    it('should handle different agent types correctly', async () => {
      const testCases = [
        { agentType: 'coder', expectedRole: 'coder' },
        { agentType: 'reviewer', expectedRole: 'reviewer' },
        { agentType: 'tester', expectedRole: 'tester' },
        { agentType: 'documenter', expectedRole: 'documenter' },
        { agentType: 'coordinator', expectedRole: 'primary' }
      ];
      
      const mockExecution: ProcessExecution = {
        id: 'exec-123',
        processId: 'proc-123',
        processVersion: '1.0.0',
        status: 'running',
        triggeredBy: 'manual',
        startedAt: new Date().toISOString(),
        variables: {},
        activityResults: [],
        logs: []
      };
      
      for (const testCase of testCases) {
        const activity: Activity = {
          id: `agent-${testCase.agentType}`,
          type: 'agent',
          name: `${testCase.agentType} task`,
          config: {
            agentType: testCase.agentType,
            agentTask: `Task for ${testCase.agentType}`
          }
        };
        
        const result = await activityExecutor.execute(activity, {}, mockExecution);
        
        expect(result.agentType).toBe(testCase.agentType);
        
        const agent = agentOrchestrator.getAgentState(result.agentId);
        expect(agent?.role).toBe(testCase.expectedRole);
      }
    });
    
    it('should handle agent activity errors gracefully', async () => {
      const invalidActivity: Activity = {
        id: 'invalid-agent',
        type: 'agent',
        name: 'Invalid Agent Activity',
        config: {
          // Missing required agentType and agentTask
        }
      };
      
      const mockExecution: ProcessExecution = {
        id: 'exec-123',
        processId: 'proc-123',
        processVersion: '1.0.0',
        status: 'running',
        triggeredBy: 'manual',
        startedAt: new Date().toISOString(),
        variables: {},
        activityResults: [],
        logs: []
      };
      
      await expect(activityExecutor.execute(invalidActivity, {}, mockExecution))
        .rejects.toThrow('Agent activity missing agentType in config');
    });
    
    it('should use custom capabilities when provided', async () => {
      const activity: Activity = {
        id: 'custom-agent',
        type: 'agent',
        name: 'Custom Agent Task',
        config: {
          agentType: 'researcher',
          agentTask: 'Research market trends',
          agentCapabilities: ['market-research', 'data-analysis', 'reporting']
        }
      };
      
      const mockExecution: ProcessExecution = {
        id: 'exec-123',
        processId: 'proc-123',
        processVersion: '1.0.0',
        status: 'running',
        triggeredBy: 'manual',
        startedAt: new Date().toISOString(),
        variables: {},
        activityResults: [],
        logs: []
      };
      
      const result = await activityExecutor.execute(activity, {}, mockExecution);
      
      expect(result.agentType).toBe('researcher');
      expect(result.taskCompleted).toBeDefined();
    });
    
    it('should pass context to agent tasks', async () => {
      const activity: Activity = {
        id: 'context-agent',
        type: 'agent',
        name: 'Agent with Context',
        config: {
          agentType: 'coder',
          agentTask: 'Implement new feature',
          processContext: {
            framework: 'React',
            language: 'TypeScript',
            testingFramework: 'Jest'
          }
        }
      };
      
      const inputs = {
        featureDescription: 'User authentication system',
        requirements: ['OAuth2', 'JWT tokens', 'Role-based access']
      };
      
      const mockExecution: ProcessExecution = {
        id: 'exec-123',
        processId: 'proc-123',
        processVersion: '1.0.0',
        status: 'running',
        triggeredBy: 'manual',
        startedAt: new Date().toISOString(),
        variables: { projectType: 'web-app' },
        activityResults: [],
        logs: []
      };
      
      const result = await activityExecutor.execute(activity, inputs, mockExecution);
      
      expect(result.agentType).toBe('coder');
      expect(result.results).toBeDefined();
      expect(result.executionSummary).toContain('Implement new feature');
    });
  });
  
  describe('Agent Orchestration Session Management', () => {
    it('should create orchestration session for complex workflows', async () => {
      // Create a session for multi-agent collaboration
      const session = agentOrchestrator.createSession(
        'Feature Development Session',
        'Implement user authentication feature with full testing'
      );
      
      expect(session).toMatchObject({
        id: expect.any(String),
        name: 'Feature Development Session',
        goal: 'Implement user authentication feature with full testing',
        status: 'planning',
        agents: [],
        tasks: [],
        conversations: []
      });
    });
    
    it('should handle agent communication in sessions', async () => {
      const session = agentOrchestrator.createSession(
        'Code Review Session',
        'Review and improve code quality'
      );
      
      // Create agents for the session
      const coderTemplate = (activityExecutor as any).getAgentTemplate('coder');
      const reviewerTemplate = (activityExecutor as any).getAgentTemplate('reviewer');
      
      const coder = agentOrchestrator.createAgent(coderTemplate, 'feature-coder');
      const reviewer = agentOrchestrator.createAgent(reviewerTemplate, 'code-reviewer');
      
      // Test agent communication
      await agentOrchestrator.sendMessage(
        coder.id,
        reviewer.id,
        'Please review this implementation',
        'query'
      );
      
      const conversations = agentOrchestrator.getConversationHistory([coder.id, reviewer.id]);
      expect(conversations).toHaveLength(1);
      expect(conversations[0].messages).toHaveLength(2); // Query and response
      expect(conversations[0].messages[0].content).toBe('Please review this implementation');
    });
    
    it('should execute multi-step agent workflows', async () => {
      const session = agentOrchestrator.createSession(
        'Full Development Cycle',
        'Complete feature development from analysis to deployment'
      );
      
      // Create tasks that simulate a full development workflow
      const analysisTask = agentOrchestrator.createTask(
        'Analyze requirements and create technical specification',
        ['research', 'analysis', 'documentation'],
        []
      );
      
      const codingTask = agentOrchestrator.createTask(
        'Implement the feature based on specifications',
        ['coding', 'debugging'],
        [analysisTask.id]
      );
      
      const testingTask = agentOrchestrator.createTask(
        'Create comprehensive tests for the implementation',
        ['testing', 'automation'],
        [codingTask.id]
      );
      
      // Execute the workflow
      const analysisResult = await agentOrchestrator.executeTask(analysisTask.id);
      expect(analysisResult.success).toBe(true);
      
      const codingResult = await agentOrchestrator.executeTask(codingTask.id);
      expect(codingResult.success).toBe(true);
      
      const testingResult = await agentOrchestrator.executeTask(testingTask.id);
      expect(testingResult.success).toBe(true);
      
      agentOrchestrator.completeSession();
    });
  });
  
  describe('Process-Agent Integration', () => {
    it('should integrate agent activities in process execution', async () => {
      const process: ProcessDefinition = {
        id: 'agent-process-1',
        name: 'AI-Powered Development Process',
        version: '1.0.0',
        triggers: [],
        activities: [
          {
            id: 'research-activity',
            type: 'agent',
            name: 'Research Requirements',
            config: {
              agentType: 'researcher',
              agentTask: 'Research user requirements and create specification'
            }
          },
          {
            id: 'code-activity',
            type: 'agent',
            name: 'Implement Feature',
            config: {
              agentType: 'coder',
              agentTask: 'Implement feature based on research findings'
            }
          },
          {
            id: 'review-activity',
            type: 'agent',
            name: 'Code Review',
            config: {
              agentType: 'reviewer',
              agentTask: 'Review implementation for quality and security'
            }
          }
        ],
        variables: {},
        metadata: {
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
          executionCount: 0
        }
      };
      
      const execution = await processEngine.executeProcess(process);
      
      expect(execution.status).toBe('completed'); // Process completes after executing all activities
      expect(execution.processId).toBe(process.id);
      
      // Verify that each agent activity would be properly executed
      // (In a real test, we would wait for completion and check results)
    });
  });
});