import { describe, it, expect, beforeEach } from 'vitest';
import Fastify from 'fastify';
import { insightsRoutes } from '../../../../../modules/statistics-insights/interface-adapters/controllers/http/insights.routes.js';
import { InMemoryStatsGateway } from '../../../../../tests/stubs/stats.stub.js';
import { InMemoryInsightsGateway } from '../../../../../tests/stubs/insights.stub.js';
import { InMemoryReviewFileGateway } from '../../../../../tests/stubs/reviewFile.stub.js';
import { InMemoryReviewRequestTrackingGateway } from '../../../../../tests/stubs/reviewRequestTracking.stub.js';
import { createStubLogger } from '../../../../../tests/stubs/logger.stub.js';
import { ProjectStatsFactory, ReviewStatsFactory } from '../../../../../tests/factories/projectStats.factory.js';
const validAiResult = {
    developers: [
        {
            developerName: 'alice',
            title: 'Le Chirurgien du Code',
            titleExplanation: 'Precise and methodical',
            strengths: ['Excellent test coverage'],
            weaknesses: ['Slow review turnaround'],
            recommendations: ['Automate repetitive checks'],
            summary: 'Alice is a meticulous developer.',
        },
    ],
    team: {
        summary: 'A well-balanced team.',
        strengths: ['Strong testing culture'],
        weaknesses: ['Documentation gaps'],
        recommendations: ['Establish review guidelines'],
        dynamics: 'Good team dynamics.',
    },
    generatedAt: '2026-03-15T10:00:00Z',
};
function createSuccessfulClaudeInvoker() {
    return async () => JSON.stringify(validAiResult);
}
describe('insights routes', () => {
    let app;
    let statsGateway;
    let insightsGateway;
    let reviewFileGateway;
    let reviewRequestTrackingGateway;
    beforeEach(async () => {
        app = Fastify();
        statsGateway = new InMemoryStatsGateway();
        insightsGateway = new InMemoryInsightsGateway();
        reviewFileGateway = new InMemoryReviewFileGateway();
        reviewRequestTrackingGateway = new InMemoryReviewRequestTrackingGateway();
        await app.register(insightsRoutes, {
            statsGateway,
            insightsGateway,
            reviewFileGateway,
            reviewRequestTrackingGateway,
            logger: createStubLogger(),
            claudeInvoker: createSuccessfulClaudeInvoker(),
            language: 'fr',
        });
        await app.ready();
    });
    it('should return 400 when path query parameter is missing', async () => {
        const response = await app.inject({
            method: 'GET',
            url: '/api/insights',
        });
        expect(response.statusCode).toBe(400);
        const body = response.json();
        expect(body.error).toBeDefined();
    });
    it('should return empty insights when project has no stats', async () => {
        const response = await app.inject({
            method: 'GET',
            url: '/api/insights?path=/nonexistent/project',
        });
        expect(response.statusCode).toBe(200);
        const body = response.json();
        expect(body.isEmpty).toBe(true);
        expect(body.developers).toEqual([]);
    });
    it('should return empty insights when project has no reviews', async () => {
        statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.create({ reviews: [] }));
        const response = await app.inject({
            method: 'GET',
            url: '/api/insights?path=/test/project',
        });
        expect(response.statusCode).toBe(200);
        const body = response.json();
        expect(body.isEmpty).toBe(true);
    });
    it('should return developer insights for eligible developers', async () => {
        const reviews = Array.from({ length: 6 }, (_, index) => ReviewStatsFactory.create({
            id: `alice-${index}`,
            assignedBy: 'alice',
            mrNumber: index + 1,
            score: 8,
            blocking: 0,
            warnings: 1,
            duration: 60000,
        }));
        statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
        const response = await app.inject({
            method: 'GET',
            url: '/api/insights?path=/test/project',
        });
        expect(response.statusCode).toBe(200);
        const body = response.json();
        expect(body.isEmpty).toBe(false);
        expect(body.developers).toHaveLength(1);
        expect(body.developers[0].developerName).toBe('alice');
    });
    it('should return team insights section', async () => {
        const aliceReviews = Array.from({ length: 5 }, (_, index) => ReviewStatsFactory.create({
            id: `alice-${index}`,
            assignedBy: 'alice',
            mrNumber: index + 1,
            score: 8,
        }));
        const bobReviews = Array.from({ length: 5 }, (_, index) => ReviewStatsFactory.create({
            id: `bob-${index}`,
            assignedBy: 'bob',
            mrNumber: index + 10,
            score: 6,
        }));
        statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews([...aliceReviews, ...bobReviews]));
        const response = await app.inject({
            method: 'GET',
            url: '/api/insights?path=/test/project',
        });
        expect(response.statusCode).toBe(200);
        const body = response.json();
        expect(body.team).toBeDefined();
        expect(body.team.developerCount).toBe(2);
        expect(body.team.averageLevels).toBeDefined();
    });
    it('should reject paths with directory traversal', async () => {
        const response = await app.inject({
            method: 'GET',
            url: '/api/insights?path=/../etc/passwd',
        });
        expect(response.statusCode).toBe(400);
    });
    it('should reject relative paths', async () => {
        const response = await app.inject({
            method: 'GET',
            url: '/api/insights?path=relative/path',
        });
        expect(response.statusCode).toBe(400);
    });
    describe('persistence behavior', () => {
        it('should save persisted insights data after computing', async () => {
            const reviews = Array.from({ length: 6 }, (_, index) => ReviewStatsFactory.create({
                id: `alice-persist-${index}`,
                assignedBy: 'alice',
                mrNumber: index + 1,
                score: 8,
                blocking: 0,
                warnings: 1,
                duration: 60000,
            }));
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            await app.inject({
                method: 'GET',
                url: '/api/insights?path=/test/project',
            });
            const persisted = insightsGateway.loadPersistedInsights('/test/project');
            expect(persisted).not.toBeNull();
            expect(persisted?.developers).toHaveLength(1);
            expect(persisted?.processedReviewIds).toHaveLength(6);
        });
        it('should use persisted data on subsequent requests', async () => {
            const reviews = Array.from({ length: 6 }, (_, index) => ReviewStatsFactory.create({
                id: `alice-${index}`,
                assignedBy: 'alice',
                mrNumber: index + 1,
                score: 8,
            }));
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            await app.inject({
                method: 'GET',
                url: '/api/insights?path=/test/project',
            });
            const newReview = ReviewStatsFactory.create({
                id: 'alice-new',
                assignedBy: 'alice',
                mrNumber: 100,
                score: 9,
            });
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews([...reviews, newReview]));
            const response = await app.inject({
                method: 'GET',
                url: '/api/insights?path=/test/project',
            });
            expect(response.statusCode).toBe(200);
            const body = response.json();
            expect(body.developers[0].reviewCount).toBe(7);
            const persisted = insightsGateway.loadPersistedInsights('/test/project');
            expect(persisted?.processedReviewIds).toContain('alice-new');
        });
        it('should not save persisted data when no stats exist', async () => {
            await app.inject({
                method: 'GET',
                url: '/api/insights?path=/empty/project',
            });
            const persisted = insightsGateway.loadPersistedInsights('/empty/project');
            expect(persisted).toBeNull();
        });
    });
    describe('POST /api/insights/generate', () => {
        it('should return 400 when path is missing from body', async () => {
            const response = await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: {},
            });
            expect(response.statusCode).toBe(400);
        });
        it('should return 400 when path is a relative path', async () => {
            const response = await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: { path: 'relative/path' },
            });
            expect(response.statusCode).toBe(400);
        });
        it('should return 400 when path contains directory traversal', async () => {
            const response = await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: { path: '/../etc/passwd' },
            });
            expect(response.statusCode).toBe(400);
        });
        it('should return AI insights when project has stats', async () => {
            const reviews = [
                ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
            ];
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            const response = await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: { path: '/test/project' },
            });
            expect(response.statusCode).toBe(200);
            const body = response.json();
            expect(body.developers).toBeDefined();
            expect(body.team).toBeDefined();
            expect(body.generatedAt).toBeDefined();
        });
        it('should persist AI insights after generation', async () => {
            const reviews = [
                ReviewStatsFactory.create({ id: 'r1', assignedBy: 'alice', mrNumber: 1, score: 8 }),
            ];
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: { path: '/test/project' },
            });
            const persisted = insightsGateway.loadPersistedInsights('/test/project');
            expect(persisted).not.toBeNull();
            expect(persisted?.aiInsights).not.toBeNull();
            expect(persisted?.aiInsights?.developers).toHaveLength(1);
        });
        it('should return 500 when project has no stats', async () => {
            const response = await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: { path: '/empty/project' },
            });
            expect(response.statusCode).toBe(500);
        });
    });
    describe('GET /api/insights with aiInsights', () => {
        it('should return null aiInsights when not generated', async () => {
            const reviews = Array.from({ length: 6 }, (_, index) => ReviewStatsFactory.create({
                id: `alice-${index}`,
                assignedBy: 'alice',
                mrNumber: index + 1,
                score: 8,
                blocking: 0,
                warnings: 1,
                duration: 60000,
            }));
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            const response = await app.inject({
                method: 'GET',
                url: '/api/insights?path=/test/project',
            });
            expect(response.statusCode).toBe(200);
            const body = response.json();
            expect(body.aiInsights).toBeNull();
        });
        it('should return aiInsights when previously generated', async () => {
            const reviews = Array.from({ length: 6 }, (_, index) => ReviewStatsFactory.create({
                id: `alice-${index}`,
                assignedBy: 'alice',
                mrNumber: index + 1,
                score: 8,
                blocking: 0,
                warnings: 1,
                duration: 60000,
            }));
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: { path: '/test/project' },
            });
            const response = await app.inject({
                method: 'GET',
                url: '/api/insights?path=/test/project',
            });
            expect(response.statusCode).toBe(200);
            const body = response.json();
            expect(body.aiInsights).not.toBeNull();
            expect(body.aiInsights.developers).toHaveLength(1);
        });
        it('should return hasNewReviewsSinceAiGeneration false when no new reviews', async () => {
            const reviews = Array.from({ length: 6 }, (_, index) => ReviewStatsFactory.create({
                id: `alice-${index}`,
                assignedBy: 'alice',
                mrNumber: index + 1,
                score: 8,
                blocking: 0,
                warnings: 1,
                duration: 60000,
            }));
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: { path: '/test/project' },
            });
            const response = await app.inject({
                method: 'GET',
                url: '/api/insights?path=/test/project',
            });
            expect(response.statusCode).toBe(200);
            const body = response.json();
            expect(body.hasNewReviewsSinceAiGeneration).toBe(false);
        });
        it('should return hasNewReviewsSinceAiGeneration true when new reviews arrived', async () => {
            const reviews = Array.from({ length: 6 }, (_, index) => ReviewStatsFactory.create({
                id: `alice-${index}`,
                assignedBy: 'alice',
                mrNumber: index + 1,
                score: 8,
                blocking: 0,
                warnings: 1,
                duration: 60000,
            }));
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            await app.inject({
                method: 'POST',
                url: '/api/insights/generate',
                payload: { path: '/test/project' },
            });
            const newReview = ReviewStatsFactory.create({
                id: 'alice-new-after-ai',
                assignedBy: 'alice',
                mrNumber: 100,
                score: 9,
            });
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews([...reviews, newReview]));
            const response = await app.inject({
                method: 'GET',
                url: '/api/insights?path=/test/project',
            });
            expect(response.statusCode).toBe(200);
            const body = response.json();
            expect(body.hasNewReviewsSinceAiGeneration).toBe(true);
        });
        it('should return hasNewReviewsSinceAiGeneration false when no AI insights exist', async () => {
            const reviews = Array.from({ length: 6 }, (_, index) => ReviewStatsFactory.create({
                id: `alice-${index}`,
                assignedBy: 'alice',
                mrNumber: index + 1,
                score: 8,
                blocking: 0,
                warnings: 1,
                duration: 60000,
            }));
            statsGateway.saveProjectStats('/test/project', ProjectStatsFactory.withReviews(reviews));
            const response = await app.inject({
                method: 'GET',
                url: '/api/insights?path=/test/project',
            });
            expect(response.statusCode).toBe(200);
            const body = response.json();
            expect(body.hasNewReviewsSinceAiGeneration).toBe(false);
        });
    });
});
//# sourceMappingURL=insights.routes.test.js.map