"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const langchain_service_1 = require("../../../src/services/ai/langchain.service");
const openai_1 = require("@langchain/openai");
// Mock dependencies
jest.mock('@langchain/openai');
jest.mock('langchain/memory');
describe('LangChainService', () => {
    let service;
    let mockConfig;
    let mockLLM;
    beforeEach(() => {
        jest.clearAllMocks();
        mockConfig = {
            openai: {
                apiKey: 'test-api-key',
                model: 'gpt-4-turbo',
                maxTokens: 4096,
                temperature: 0.7,
                retryAttempts: 3,
                retryDelayMs: 100,
                timeout: 30000,
            },
            langchain: {
                verbose: false,
                cacheEnabled: true,
                cacheTTL: 3600,
                maxConcurrency: 5,
                memoryBufferSize: 10,
            },
            rateLimit: {
                maxRequestsPerMinute: 60,
                maxTokensPerMinute: 90000,
                maxRequestsPerDay: 10000,
            },
            monitoring: {
                trackTokenUsage: true,
                logLevel: 'info',
                metricsEnabled: true,
            },
        };
        service = new langchain_service_1.LangChainService(mockConfig);
        mockLLM = openai_1.ChatOpenAI.mock.instances[0];
    });
    describe('createCompletionChain', () => {
        it('should create a basic completion chain', () => {
            const systemPrompt = 'You are a helpful assistant';
            const chain = service.createCompletionChain(systemPrompt);
            expect(chain).toBeDefined();
            expect(chain.invoke).toBeDefined();
        });
        it('should create a chain with custom options', () => {
            const systemPrompt = 'You are a code reviewer';
            const options = {
                temperature: 0.3,
                maxTokens: 2000,
            };
            const chain = service.createCompletionChain(systemPrompt, options);
            expect(chain).toBeDefined();
        });
    });
    describe('createConversationChain', () => {
        it('should create a conversation chain with memory', () => {
            const systemPrompt = 'You are a project assistant';
            const sessionId = 'test-session-123';
            const chain = service.createConversationChain(systemPrompt, sessionId);
            expect(chain).toBeDefined();
            expect(chain.invoke).toBeDefined();
        });
        it('should reuse memory for same session', () => {
            const systemPrompt = 'You are a helper';
            const sessionId = 'same-session';
            const chain1 = service.createConversationChain(systemPrompt, sessionId);
            const chain2 = service.createConversationChain(systemPrompt, sessionId);
            // Both should use the same memory instance
            expect(chain1).toBeDefined();
            expect(chain2).toBeDefined();
        });
    });
    describe('createTaskBreakdownAgent', () => {
        it('should create a task breakdown agent', () => {
            const agent = service.createTaskBreakdownAgent();
            expect(agent).toBeDefined();
            expect(agent.invoke).toBeDefined();
        });
        it('should create agent with custom options', () => {
            const options = {
                temperature: 0.2,
                maxTokens: 3000,
            };
            const agent = service.createTaskBreakdownAgent(options);
            expect(agent).toBeDefined();
        });
    });
    describe('breakdownTask', () => {
        it('should breakdown a task successfully', async () => {
            const mockBreakdown = {
                title: 'Implement User Authentication',
                description: 'Create a secure authentication system',
                subtasks: [
                    {
                        id: '1',
                        title: 'Setup database schema',
                        description: 'Create user tables',
                        estimatedHours: 2,
                        dependencies: [],
                    },
                    {
                        id: '2',
                        title: 'Implement JWT tokens',
                        description: 'Create JWT generation and validation',
                        estimatedHours: 3,
                        dependencies: ['1'],
                    },
                ],
                estimatedTotalHours: 5,
                complexity: 'major',
                suggestedApproach: 'Use JWT with refresh tokens',
            };
            // Mock the chain invoke method
            const mockChain = {
                invoke: jest.fn().mockResolvedValue(mockBreakdown),
            };
            jest.spyOn(service, 'createTaskBreakdownAgent').mockReturnValue(mockChain);
            const result = await service.breakdownTask('Implement user authentication', 'Web application context');
            expect(result).toEqual(mockBreakdown);
            expect(mockChain.invoke).toHaveBeenCalledWith({
                taskDescription: 'Implement user authentication',
                context: 'Web application context',
            });
        });
        it('should handle breakdown errors', async () => {
            const mockChain = {
                invoke: jest.fn().mockRejectedValue(new Error('AI error')),
            };
            jest.spyOn(service, 'createTaskBreakdownAgent').mockReturnValue(mockChain);
            await expect(service.breakdownTask('Test task')).rejects.toThrow('Failed to breakdown task');
        });
        it('should validate breakdown schema', async () => {
            const invalidBreakdown = {
                title: 'Test Task',
                // Missing required fields
            };
            const mockChain = {
                invoke: jest.fn().mockResolvedValue(invalidBreakdown),
            };
            jest.spyOn(service, 'createTaskBreakdownAgent').mockReturnValue(mockChain);
            await expect(service.breakdownTask('Test task')).rejects.toThrow();
        });
    });
    describe('createTaskFromNaturalLanguage', () => {
        it('should create task from natural language', async () => {
            const mockTask = {
                title: 'Fix login bug',
                description: 'Users cannot login with email',
                priority: 'high',
                complexity: 'minor',
                acceptanceCriteria: ['Login works with email', 'Error messages shown'],
                tags: ['bug', 'auth'],
                estimatedHours: 2,
            };
            const mockChain = {
                invoke: jest.fn().mockResolvedValue(mockTask),
            };
            jest.spyOn(service, 'createTaskCreationChain').mockReturnValue(mockChain);
            const result = await service.createTaskFromNaturalLanguage('Fix the bug where users cant login with their email', 'Authentication module');
            expect(result).toMatchObject(mockTask);
            expect(result.executionTimeMs).toBeGreaterThanOrEqual(0);
        });
    });
    describe('getTaskSuggestions', () => {
        it('should generate task suggestions', async () => {
            const mockSuggestions = `
        1. Implement user profile page - Users need to view and edit profiles
        2. Add password reset - Security requirement for production
        3. Setup email notifications - Keep users informed
      `;
            const mockChain = {
                invoke: jest.fn().mockResolvedValue(mockSuggestions),
            };
            jest.spyOn(service, 'createTaskSuggestionChain').mockReturnValue(mockChain);
            const result = await service.getTaskSuggestions({
                projectDescription: 'User management system',
                completedTasks: ['User registration', 'Login'],
                currentTasks: ['Dashboard'],
                goals: ['Complete MVP', 'Launch beta'],
            });
            expect(result).toContain('user profile');
            expect(result).toContain('password reset');
        });
    });
    describe('memory management', () => {
        it('should add messages to memory', async () => {
            const sessionId = 'test-session';
            // Mock the memory chatHistory
            const mockMemory = {
                chatHistory: {
                    addMessage: jest.fn(),
                    getMessages: jest.fn().mockResolvedValue([
                        { _getType: () => 'human', content: 'Hello' },
                        { _getType: () => 'ai', content: 'Hi there!' },
                    ]),
                },
                clear: jest.fn(),
            };
            // Store mock in service's memory map for getMemorySummary to work
            service.memories.set(sessionId, mockMemory);
            await service.addToMemory(sessionId, 'Hello', 'human');
            await service.addToMemory(sessionId, 'Hi there!', 'ai');
            expect(mockMemory.chatHistory.addMessage).toHaveBeenCalledTimes(2);
            const summary = await service.getMemorySummary(sessionId);
            expect(summary).toContain('Hello');
            expect(summary).toContain('Hi there!');
        });
        it('should clear memory for session', async () => {
            const sessionId = 'test-clear';
            const mockMemory = {
                chatHistory: {
                    addMessage: jest.fn(),
                    getMessages: jest.fn().mockResolvedValue([]),
                },
                clear: jest.fn(),
            };
            jest.spyOn(service, 'getOrCreateMemory').mockReturnValue(mockMemory);
            await service.addToMemory(sessionId, 'Test message', 'human');
            service.clearMemory(sessionId);
            const summary = await service.getMemorySummary(sessionId);
            expect(summary).toBeNull();
        });
        it('should clear all memories', async () => {
            const mockMemory1 = {
                chatHistory: {
                    addMessage: jest.fn(),
                    getMessages: jest.fn().mockResolvedValue([]),
                },
                clear: jest.fn(),
            };
            const mockMemory2 = {
                chatHistory: {
                    addMessage: jest.fn(),
                    getMessages: jest.fn().mockResolvedValue([]),
                },
                clear: jest.fn(),
            };
            jest.spyOn(service, 'getOrCreateMemory')
                .mockReturnValueOnce(mockMemory1)
                .mockReturnValueOnce(mockMemory2);
            await service.addToMemory('session1', 'Message 1', 'human');
            await service.addToMemory('session2', 'Message 2', 'human');
            service.clearAllMemories();
            const summary1 = await service.getMemorySummary('session1');
            const summary2 = await service.getMemorySummary('session2');
            expect(summary1).toBeNull();
            expect(summary2).toBeNull();
        });
    });
    describe('streaming', () => {
        it('should handle streaming responses', async () => {
            const mockChain = {
                stream: jest.fn().mockImplementation(async function* () {
                    yield 'Hello';
                    yield ' ';
                    yield 'world';
                }),
            };
            const chunks = [];
            const result = await service.streamChain(mockChain, { input: 'test' }, (chunk) => chunks.push(chunk));
            expect(result).toBe('Hello world');
            expect(chunks).toEqual(['Hello', ' ', 'world']);
        });
        it('should handle streaming with objects', async () => {
            const mockChain = {
                stream: jest.fn().mockImplementation(async function* () {
                    yield { content: 'Part 1' };
                    yield { content: ' Part 2' };
                }),
            };
            const chunks = [];
            const result = await service.streamChain(mockChain, { input: 'test' }, (chunk) => chunks.push(chunk));
            expect(result).toBe('Part 1 Part 2');
            expect(chunks).toEqual(['Part 1', ' Part 2']);
        });
    });
    describe('testConnection', () => {
        it('should return true when connection succeeds', async () => {
            const mockChain = {
                invoke: jest.fn().mockResolvedValue('OK, I can hear you!'),
            };
            jest.spyOn(service, 'createCompletionChain').mockReturnValue(mockChain);
            const result = await service.testConnection();
            expect(result).toBe(true);
        });
        it('should return false when connection fails', async () => {
            const mockChain = {
                invoke: jest.fn().mockRejectedValue(new Error('Connection failed')),
            };
            jest.spyOn(service, 'createCompletionChain').mockReturnValue(mockChain);
            const result = await service.testConnection();
            expect(result).toBe(false);
        });
    });
    describe('TaskBreakdownSchema validation', () => {
        it('should validate correct task breakdown', () => {
            const validBreakdown = {
                title: 'Main Task',
                description: 'Task description',
                subtasks: [
                    {
                        id: '1',
                        title: 'Subtask 1',
                        description: 'Subtask description',
                        estimatedHours: 2,
                        dependencies: [],
                    },
                ],
                estimatedTotalHours: 2,
                complexity: 'minor',
                suggestedApproach: 'Step by step',
            };
            const result = langchain_service_1.TaskBreakdownSchema.parse(validBreakdown);
            expect(result).toEqual(validBreakdown);
        });
        it('should reject invalid complexity', () => {
            const invalidBreakdown = {
                title: 'Task',
                description: 'Description',
                subtasks: [],
                complexity: 'invalid',
            };
            expect(() => langchain_service_1.TaskBreakdownSchema.parse(invalidBreakdown)).toThrow();
        });
        it('should allow optional fields', () => {
            const minimalBreakdown = {
                title: 'Task',
                description: 'Description',
                subtasks: [],
                complexity: 'trivial',
            };
            const result = langchain_service_1.TaskBreakdownSchema.parse(minimalBreakdown);
            expect(result).toEqual(minimalBreakdown);
        });
    });
});
//# sourceMappingURL=langchain.service.test.js.map