import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { backfillDiffStats } from '../../../../modules/statistics-insights/usecases/stats/backfillDiffStats.usecase.js';
import { InMemoryStatsGateway } from '../../../../tests/stubs/stats.stub.js';
import { StubDiffStatsFetchGateway } from '../../../../tests/stubs/diffStatsFetch.stub.js';
import { ProjectStatsFactory, ReviewStatsFactory } from '../../../../tests/factories/projectStats.factory.js';
describe('backfillDiffStats', () => {
    beforeEach(() => {
        vi.useFakeTimers();
    });
    afterEach(() => {
        vi.useRealTimers();
    });
    it('should fetch diff stats for reviews with undefined diffStats', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const reviews = [
            ReviewStatsFactory.create({ id: 'r1', mrNumber: 10 }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        diffStatsFetchGateway.setResponse(10, { commitsCount: 3, additions: 100, deletions: 20 });
        const promise = backfillDiffStats({ projectPath: '/test/project', batchSize: 10, batchDelayMs: 0 }, { statsGateway, diffStatsFetchGateway, logger: { warn: vi.fn() } });
        await vi.runAllTimersAsync();
        const result = await promise;
        expect(result.completed).toBe(1);
        expect(result.failed).toBe(0);
        expect(result.status).toBe('completed');
        const saved = statsGateway.loadProjectStats('/test/project');
        expect(saved?.reviews[0].diffStats).toEqual({ commitsCount: 3, additions: 100, deletions: 20 });
    });
    it('should skip reviews that already have non-null diffStats', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const reviews = [
            ReviewStatsFactory.withDiffStats({ commitsCount: 2, additions: 50, deletions: 10 }, { id: 'r1', mrNumber: 10 }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        const promise = backfillDiffStats({ projectPath: '/test/project', batchSize: 10, batchDelayMs: 0 }, { statsGateway, diffStatsFetchGateway, logger: { warn: vi.fn() } });
        await vi.runAllTimersAsync();
        const result = await promise;
        expect(result.total).toBe(0);
        expect(result.completed).toBe(0);
        expect(result.status).toBe('completed');
        expect(diffStatsFetchGateway.fetchCallCount).toBe(0);
    });
    it('should skip reviews that have diffStats === null (already attempted)', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const reviews = [
            ReviewStatsFactory.create({ id: 'r1', mrNumber: 10, diffStats: null }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        const promise = backfillDiffStats({ projectPath: '/test/project', batchSize: 10, batchDelayMs: 0 }, { statsGateway, diffStatsFetchGateway, logger: { warn: vi.fn() } });
        await vi.runAllTimersAsync();
        const result = await promise;
        expect(result.total).toBe(0);
        expect(result.completed).toBe(0);
        expect(diffStatsFetchGateway.fetchCallCount).toBe(0);
    });
    it('should call onProgress after each review', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const reviews = [
            ReviewStatsFactory.create({ id: 'r1', mrNumber: 10 }),
            ReviewStatsFactory.create({ id: 'r2', mrNumber: 11 }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        diffStatsFetchGateway.setResponse(10, { commitsCount: 1, additions: 10, deletions: 5 });
        diffStatsFetchGateway.setResponse(11, { commitsCount: 2, additions: 20, deletions: 10 });
        const progressUpdates = [];
        const promise = backfillDiffStats({
            projectPath: '/test/project',
            batchSize: 10,
            batchDelayMs: 0,
            onProgress: (progress) => progressUpdates.push({ ...progress }),
        }, { statsGateway, diffStatsFetchGateway, logger: { warn: vi.fn() } });
        await vi.runAllTimersAsync();
        await promise;
        expect(progressUpdates.length).toBe(2);
        expect(progressUpdates[0].completed).toBe(1);
        expect(progressUpdates[1].completed).toBe(2);
    });
    it('should handle fetch failure gracefully', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const logger = { warn: vi.fn() };
        const reviews = [
            ReviewStatsFactory.create({ id: 'r1', mrNumber: 10 }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        diffStatsFetchGateway.setFailure(10);
        const promise = backfillDiffStats({ projectPath: '/test/project', batchSize: 10, batchDelayMs: 0 }, { statsGateway, diffStatsFetchGateway, logger });
        await vi.runAllTimersAsync();
        const result = await promise;
        expect(result.failed).toBe(1);
        expect(result.completed).toBe(1);
        expect(result.status).toBe('completed');
        expect(logger.warn).toHaveBeenCalled();
        const saved = statsGateway.loadProjectStats('/test/project');
        expect(saved?.reviews[0].diffStats).toBeNull();
    });
    it('should return correct final progress', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const reviews = [
            ReviewStatsFactory.create({ id: 'r1', mrNumber: 10 }),
            ReviewStatsFactory.create({ id: 'r2', mrNumber: 11 }),
            ReviewStatsFactory.create({ id: 'r3', mrNumber: 12 }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        diffStatsFetchGateway.setResponse(10, { commitsCount: 1, additions: 10, deletions: 5 });
        diffStatsFetchGateway.setFailure(11);
        diffStatsFetchGateway.setResponse(12, { commitsCount: 2, additions: 20, deletions: 10 });
        const promise = backfillDiffStats({ projectPath: '/test/project', batchSize: 10, batchDelayMs: 0 }, { statsGateway, diffStatsFetchGateway, logger: { warn: vi.fn() } });
        await vi.runAllTimersAsync();
        const result = await promise;
        expect(result.total).toBe(3);
        expect(result.completed).toBe(3);
        expect(result.failed).toBe(1);
        expect(result.status).toBe('completed');
    });
    it('should save updated stats after completion', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const reviews = [
            ReviewStatsFactory.create({ id: 'r1', mrNumber: 10 }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        diffStatsFetchGateway.setResponse(10, { commitsCount: 3, additions: 100, deletions: 20 });
        const promise = backfillDiffStats({ projectPath: '/test/project', batchSize: 10, batchDelayMs: 0 }, { statsGateway, diffStatsFetchGateway, logger: { warn: vi.fn() } });
        await vi.runAllTimersAsync();
        await promise;
        const saved = statsGateway.loadProjectStats('/test/project');
        expect(saved).not.toBeNull();
        expect(saved?.reviews[0].diffStats).toEqual({ commitsCount: 3, additions: 100, deletions: 20 });
    });
    it('should return immediately when no reviews need backfill', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const reviews = [
            ReviewStatsFactory.withDiffStats({ commitsCount: 1, additions: 10, deletions: 5 }, { id: 'r1', mrNumber: 10 }),
            ReviewStatsFactory.create({ id: 'r2', mrNumber: 11, diffStats: null }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        const result = await backfillDiffStats({ projectPath: '/test/project', batchSize: 10, batchDelayMs: 0 }, { statsGateway, diffStatsFetchGateway, logger: { warn: vi.fn() } });
        expect(result.total).toBe(0);
        expect(result.status).toBe('completed');
        expect(diffStatsFetchGateway.fetchCallCount).toBe(0);
    });
    it('should process in batches with delay between them', async () => {
        const statsGateway = new InMemoryStatsGateway();
        const diffStatsFetchGateway = new StubDiffStatsFetchGateway();
        const reviews = [
            ReviewStatsFactory.create({ id: 'r1', mrNumber: 10 }),
            ReviewStatsFactory.create({ id: 'r2', mrNumber: 11 }),
            ReviewStatsFactory.create({ id: 'r3', mrNumber: 12 }),
        ];
        const projectStats = ProjectStatsFactory.create({ reviews });
        statsGateway.saveProjectStats('/test/project', projectStats);
        diffStatsFetchGateway.setResponse(10, { commitsCount: 1, additions: 10, deletions: 5 });
        diffStatsFetchGateway.setResponse(11, { commitsCount: 2, additions: 20, deletions: 10 });
        diffStatsFetchGateway.setResponse(12, { commitsCount: 3, additions: 30, deletions: 15 });
        const progressUpdates = [];
        const promise = backfillDiffStats({
            projectPath: '/test/project',
            batchSize: 2,
            batchDelayMs: 1000,
            onProgress: (progress) => progressUpdates.push({ ...progress }),
        }, { statsGateway, diffStatsFetchGateway, logger: { warn: vi.fn() } });
        await vi.advanceTimersByTimeAsync(0);
        expect(progressUpdates.length).toBe(2);
        await vi.advanceTimersByTimeAsync(1000);
        expect(progressUpdates.length).toBe(3);
        const result = await promise;
        expect(result.total).toBe(3);
        expect(result.completed).toBe(3);
    });
});
//# sourceMappingURL=backfillDiffStats.usecase.test.js.map