"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
const ai_task_service_1 = require("../../../src/services/ai/ai-task.service");
// Mock dependencies
jest.mock('../../../src/services/ai/openai.service');
jest.mock('../../../src/services/ai/langchain.service');
jest.mock('typeorm', () => ({
    getRepository: jest.fn(),
    Entity: jest.fn(),
    PrimaryGeneratedColumn: jest.fn(),
    Column: jest.fn(),
    CreateDateColumn: jest.fn(),
    UpdateDateColumn: jest.fn(),
    Index: jest.fn(),
}));
jest.mock('ioredis');
jest.mock('rate-limiter-flexible');
describe('AITaskService', () => {
    let service;
    let mockOpenAIService;
    let mockLangChainService;
    let mockRedis;
    let mockRepository;
    beforeEach(() => {
        jest.clearAllMocks();
        // Mock repository
        mockRepository = {
            create: jest.fn(),
            save: jest.fn(),
            count: jest.fn().mockResolvedValue(10),
        };
        // Mock getRepository
        require('typeorm').getRepository.mockReturnValue(mockRepository);
        // Create mock services
        mockOpenAIService = {
            complete: jest.fn(),
            testConnection: jest.fn().mockResolvedValue(true),
            getUsageStats: jest.fn().mockReturnValue({
                requestCount: 5,
                totalTokensUsed: 1000,
                estimatedCost: 0.02,
            }),
        };
        mockLangChainService = {
            createTaskFromNaturalLanguage: jest.fn(),
            breakdownTask: jest.fn(),
            getTaskSuggestions: jest.fn(),
            testConnection: jest.fn().mockResolvedValue(true),
        };
        mockRedis = {
            get: jest.fn(),
            set: jest.fn(),
            del: jest.fn(),
            keys: jest.fn().mockResolvedValue([]),
            ping: jest.fn().mockResolvedValue('PONG'),
        };
        // Create service instance
        service = new ai_task_service_1.AITaskService(mockOpenAIService, mockLangChainService, mockRedis);
    });
    describe('createTaskFromNaturalLanguage', () => {
        it('should create a task from natural language', async () => {
            const mockTask = {
                title: 'Fix login bug',
                description: 'Users cannot login with email',
                priority: 'high',
                complexity: 'minor',
                estimatedHours: 2,
                executionTimeMs: 150,
            };
            mockLangChainService.createTaskFromNaturalLanguage.mockResolvedValue(mockTask);
            mockRedis.get.mockResolvedValue(null); // No cache hit
            const result = await service.createTaskFromNaturalLanguage({
                prompt: 'Fix the bug where users cant login with email',
                projectId: 'project-123',
            });
            expect(result).toMatchObject({
                title: 'Fix login bug',
                description: 'Users cannot login with email',
                priority: 'high',
                complexity: 'minor',
            });
            expect(mockLangChainService.createTaskFromNaturalLanguage).toHaveBeenCalledWith('Fix the bug where users cant login with email', '', { temperature: 0.5 });
            // Should cache the result
            expect(mockRedis.set).toHaveBeenCalled();
        });
        it('should enhance task with acceptance criteria if missing', async () => {
            const mockTask = {
                title: 'Add feature',
                description: 'New feature implementation',
                priority: 'medium',
                complexity: 'major',
                acceptanceCriteria: [],
            };
            mockLangChainService.createTaskFromNaturalLanguage.mockResolvedValue(mockTask);
            mockOpenAIService.complete.mockResolvedValue({
                content: '1. Feature works correctly\n2. Tests pass\n3. Documentation updated',
                promptTokens: 50,
                completionTokens: 30,
                totalTokens: 80,
                model: 'gpt-4-turbo',
                finishReason: 'stop',
                executionTimeMs: 200,
            });
            mockRedis.get.mockResolvedValue(null);
            const result = await service.createTaskFromNaturalLanguage({
                prompt: 'Add new feature',
            });
            expect(result.acceptanceCriteria).toEqual([
                'Feature works correctly',
                'Tests pass',
                'Documentation updated',
            ]);
            expect(mockOpenAIService.complete).toHaveBeenCalled();
        });
        it('should use cached result if available', async () => {
            const cachedTask = {
                title: 'Cached task',
                description: 'From cache',
                priority: 'low',
                complexity: 'trivial',
            };
            mockRedis.get.mockResolvedValue(JSON.stringify(cachedTask));
            const result = await service.createTaskFromNaturalLanguage({
                prompt: 'Create a task',
            });
            expect(result).toEqual(cachedTask);
            expect(mockLangChainService.createTaskFromNaturalLanguage).not.toHaveBeenCalled();
        });
        it('should save AI context on success', async () => {
            const mockTask = {
                title: 'New task',
                description: 'Task description',
                priority: 'medium',
                complexity: 'minor',
            };
            mockLangChainService.createTaskFromNaturalLanguage.mockResolvedValue(mockTask);
            mockRedis.get.mockResolvedValue(null);
            await service.createTaskFromNaturalLanguage({
                prompt: 'Create new task',
            });
            expect(mockRepository.create).toHaveBeenCalled();
            expect(mockRepository.save).toHaveBeenCalled();
        });
        it('should save error context on failure', async () => {
            mockLangChainService.createTaskFromNaturalLanguage.mockRejectedValue(new Error('AI service error'));
            mockRedis.get.mockResolvedValue(null);
            await expect(service.createTaskFromNaturalLanguage({
                prompt: 'Create task',
            })).rejects.toThrow('AI service error');
            expect(mockRepository.create).toHaveBeenCalledWith(expect.objectContaining({
                context_type: 'error',
                success: false,
            }));
        });
    });
    describe('breakdownTask', () => {
        it('should breakdown a task into subtasks', async () => {
            const mockBreakdown = {
                title: 'Main task',
                description: 'Task to break down',
                subtasks: [
                    { id: '1', title: 'Subtask 1', description: 'First subtask' },
                    { id: '2', title: 'Subtask 2', description: 'Second subtask' },
                ],
                complexity: 'major',
                estimatedTotalHours: 8,
            };
            mockLangChainService.breakdownTask.mockResolvedValue(mockBreakdown);
            mockRedis.get.mockResolvedValue(null);
            const result = await service.breakdownTask('task-123', 'Implement authentication system', 'Web application');
            expect(result).toEqual(mockBreakdown);
            expect(mockLangChainService.breakdownTask).toHaveBeenCalledWith('Implement authentication system', 'Web application', { temperature: 0.3, maxTokens: 2000 });
            // Should cache the result
            expect(mockRedis.set).toHaveBeenCalledWith(expect.stringContaining('ai:breakdown:task-123'), JSON.stringify(mockBreakdown), 'EX', 7200);
        });
        it('should use cached breakdown if available', async () => {
            const cachedBreakdown = {
                title: 'Cached breakdown',
                subtasks: [],
                complexity: 'minor',
            };
            mockRedis.get.mockResolvedValue(JSON.stringify(cachedBreakdown));
            const result = await service.breakdownTask('task-456', 'Task description');
            expect(result).toEqual(cachedBreakdown);
            expect(mockLangChainService.breakdownTask).not.toHaveBeenCalled();
        });
    });
    describe('getTaskSuggestions', () => {
        it('should generate task suggestions for a project', async () => {
            const mockSuggestionsText = `
        1. Implement user profile page - Users need to view and edit their profiles
        2. Add password reset functionality - Security requirement for production
        3. Setup email notifications - Keep users informed about important events
      `;
            mockLangChainService.getTaskSuggestions.mockResolvedValue(mockSuggestionsText);
            mockRedis.get.mockResolvedValue(null);
            const result = await service.getTaskSuggestions('project-123', {
                projectDescription: 'User management system',
                completedTasks: ['User registration', 'Login'],
                currentTasks: ['Dashboard'],
                goals: ['Complete MVP'],
            });
            expect(result).toHaveLength(3);
            expect(result[0]).toMatchObject({
                id: 'suggestion-1',
                title: 'Implement user profile page',
                priority: 'medium',
                confidence: 0.7,
            });
            expect(mockLangChainService.getTaskSuggestions).toHaveBeenCalledWith({
                projectDescription: 'User management system',
                completedTasks: ['User registration', 'Login'],
                currentTasks: ['Dashboard'],
                goals: ['Complete MVP'],
            }, { temperature: 0.7, maxTokens: 1500 });
        });
        it('should parse suggestions with different formats', async () => {
            const mockSuggestionsText = `
        1) First task - Description here
        2. Second task - Another description
        3) Third task
      `;
            mockLangChainService.getTaskSuggestions.mockResolvedValue(mockSuggestionsText);
            mockRedis.get.mockResolvedValue(null);
            const result = await service.getTaskSuggestions('project-456', {
                projectDescription: 'Test project',
                completedTasks: [],
                currentTasks: [],
                goals: [],
            });
            expect(result).toHaveLength(3);
            expect(result[0].title).toBe('First task');
            expect(result[1].title).toBe('Second task');
            expect(result[2].title).toBe('Third task');
        });
    });
    describe('updateTaskContext', () => {
        it('should update task context with feedback', async () => {
            await service.updateTaskContext('task-789', 'Task needs more detail');
            expect(mockRepository.create).toHaveBeenCalledWith(expect.objectContaining({
                task_id: 'task-789',
                context_type: 'feedback',
                context_data: expect.objectContaining({
                    feedback: 'Task needs more detail',
                }),
            }));
            expect(mockRepository.save).toHaveBeenCalled();
            // Should invalidate cache
            expect(mockRedis.keys).toHaveBeenCalledWith('ai:breakdown:task-789');
        });
    });
    describe('warmCache', () => {
        it('should pre-populate cache with common queries', async () => {
            const mockTask = {
                title: 'Generic task',
                description: 'Task description',
                priority: 'medium',
                complexity: 'minor',
            };
            mockLangChainService.createTaskFromNaturalLanguage.mockResolvedValue(mockTask);
            mockRedis.get.mockResolvedValue(null);
            await service.warmCache('project-123');
            // Should attempt to cache common prompts
            expect(mockLangChainService.createTaskFromNaturalLanguage).toHaveBeenCalledTimes(4);
            expect(mockLangChainService.createTaskFromNaturalLanguage).toHaveBeenCalledWith(expect.stringContaining('bug fix'), expect.any(String), expect.any(Object));
        });
        it('should handle errors during cache warming', async () => {
            mockLangChainService.createTaskFromNaturalLanguage.mockRejectedValue(new Error('Service unavailable'));
            mockRedis.get.mockResolvedValue(null);
            // Should not throw
            await expect(service.warmCache('project-456')).resolves.not.toThrow();
        });
    });
    describe('getHealthStatus', () => {
        it('should return health status of all services', async () => {
            mockOpenAIService.testConnection.mockResolvedValue(true);
            mockLangChainService.testConnection.mockResolvedValue(true);
            const health = await service.getHealthStatus();
            expect(health).toEqual({
                openai: true,
                langchain: true,
                redis: true,
                rateLimiter: true,
            });
        });
        it('should handle service failures', async () => {
            mockOpenAIService.testConnection.mockResolvedValue(false);
            mockLangChainService.testConnection.mockResolvedValue(true);
            mockRedis.ping.mockRejectedValue(new Error('Connection failed'));
            const health = await service.getHealthStatus();
            expect(health).toEqual({
                openai: false,
                langchain: true,
                redis: false,
                rateLimiter: true,
            });
        });
    });
    describe('getUsageStats', () => {
        it('should return combined usage statistics', async () => {
            const stats = await service.getUsageStats();
            expect(stats).toEqual({
                openai: {
                    requestCount: 5,
                    totalTokensUsed: 1000,
                    estimatedCost: 0.02,
                },
                totalContextsSaved: 10,
                cacheHitRate: 0.0,
            });
            expect(mockOpenAIService.getUsageStats).toHaveBeenCalled();
            expect(mockRepository.count).toHaveBeenCalled();
        });
    });
    describe('rate limiting', () => {
        it('should enforce rate limits', async () => {
            // Mock rate limiter to throw on second call
            const mockRateLimiter = {
                consume: jest.fn()
                    .mockResolvedValueOnce(undefined)
                    .mockRejectedValueOnce(new Error('Rate limit exceeded')),
            };
            service.rateLimiter = mockRateLimiter;
            const mockTask = {
                title: 'Task',
                description: 'Description',
                priority: 'medium',
                complexity: 'minor',
            };
            mockLangChainService.createTaskFromNaturalLanguage.mockResolvedValue(mockTask);
            mockRedis.get.mockResolvedValue(null);
            // First call should succeed
            await service.createTaskFromNaturalLanguage({ prompt: 'First task' });
            // Second call should fail with rate limit
            await expect(service.createTaskFromNaturalLanguage({
                prompt: 'Second task',
            })).rejects.toThrow('Rate limit exceeded');
        });
    });
    describe('caching', () => {
        it('should handle cache errors gracefully', async () => {
            mockRedis.get.mockRejectedValue(new Error('Redis error'));
            mockRedis.set.mockRejectedValue(new Error('Redis error'));
            const mockTask = {
                title: 'Task',
                description: 'Description',
                priority: 'medium',
                complexity: 'minor',
            };
            mockLangChainService.createTaskFromNaturalLanguage.mockResolvedValue(mockTask);
            // Should still work despite cache errors
            const result = await service.createTaskFromNaturalLanguage({
                prompt: 'Create task',
            });
            expect(result).toEqual(mockTask);
        });
        it('should invalidate cache patterns', async () => {
            mockRedis.keys.mockResolvedValue([
                'ai:breakdown:task-123',
                'ai:breakdown:task-123:v2',
            ]);
            await service.updateTaskContext('task-123', 'Feedback');
            expect(mockRedis.del).toHaveBeenCalledWith('ai:breakdown:task-123', 'ai:breakdown:task-123:v2');
        });
    });
});
//# sourceMappingURL=ai-task.service.test.js.map