/**
 * Tests unitaires pour MetricsService
 */

/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { MetricsService } from '../metrics';
import { prisma } from '@/services/core/database';

// Mock de prisma
vi.mock('@/services/core/database', () => ({
  prisma: {
    task: {
      count: vi.fn(),
      groupBy: vi.fn(),
    },
  },
}));

describe('MetricsService', () => {
  const userId = 'test-user-id';
  const mockDate = new Date('2024-01-15T12:00:00Z');

  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();
    vi.setSystemTime(mockDate);
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  describe('getWeeklyMetrics', () => {
    it('devrait retourner les métriques hebdomadaires', async () => {
      // Mock des comptages pour chaque jour
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(5) // completed
        .mockResolvedValueOnce(3) // inProgress
        .mockResolvedValueOnce(1) // blocked
        .mockResolvedValueOnce(2) // pending
        .mockResolvedValueOnce(2) // newTasks
        .mockResolvedValueOnce(10) // totalTasks
        .mockResolvedValueOnce(5) // completed jour 2
        .mockResolvedValueOnce(3) // inProgress jour 2
        .mockResolvedValueOnce(1) // blocked jour 2
        .mockResolvedValueOnce(2) // pending jour 2
        .mockResolvedValueOnce(2) // newTasks jour 2
        .mockResolvedValueOnce(10); // totalTasks jour 2

      // Mock pour les autres jours (répéter pour 7 jours)
      for (let i = 0; i < 5; i++) {
        vi.mocked(prisma.task.count)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0);
      }

      // Mock pour statusDistribution
      vi.mocked(prisma.task.groupBy).mockResolvedValueOnce([
        {
          status: 'done',
          _count: { status: 5 },
        },
        {
          status: 'in_progress',
          _count: { status: 3 },
        },
      ] as any);

      // Mock pour priorityBreakdown (3 priorités × 1 count each)
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(2) // high completed
        .mockResolvedValueOnce(1) // high inProgress
        .mockResolvedValueOnce(2) // medium completed
        .mockResolvedValueOnce(1) // medium inProgress
        .mockResolvedValueOnce(1) // low completed
        .mockResolvedValueOnce(1); // low inProgress

      const result = await MetricsService.getWeeklyMetrics(userId);

      expect(result).toHaveProperty('period');
      expect(result).toHaveProperty('dailyBreakdown');
      expect(result).toHaveProperty('summary');
      expect(result).toHaveProperty('statusDistribution');
      expect(result).toHaveProperty('priorityBreakdown');

      expect(result.dailyBreakdown).toHaveLength(7);
      expect(result.summary).toHaveProperty('totalTasksCompleted');
      expect(result.summary).toHaveProperty('totalTasksCreated');
      expect(result.summary).toHaveProperty('averageCompletionRate');
    });

    it('devrait calculer correctement le taux de completion', async () => {
      // Mock pour un jour avec des tâches complétées
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(5) // completed
        .mockResolvedValueOnce(0) // inProgress
        .mockResolvedValueOnce(0) // blocked
        .mockResolvedValueOnce(0) // pending
        .mockResolvedValueOnce(0) // newTasks
        .mockResolvedValueOnce(10); // totalTasks

      // Mock pour les autres jours
      for (let i = 0; i < 6; i++) {
        vi.mocked(prisma.task.count)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0);
      }

      vi.mocked(prisma.task.groupBy).mockResolvedValueOnce([] as any);

      // Mock priorityBreakdown
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0);

      const result = await MetricsService.getWeeklyMetrics(userId);

      expect(result.dailyBreakdown[0].completionRate).toBe(50);
    });

    it('devrait identifier le jour de pic de productivité', async () => {
      // Jour 1: 10 complétées
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(10)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(10);

      // Jour 2-7: 0 complétées
      for (let i = 0; i < 6; i++) {
        vi.mocked(prisma.task.count)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0)
          .mockResolvedValueOnce(0);
      }

      vi.mocked(prisma.task.groupBy).mockResolvedValueOnce([] as any);

      // Mock priorityBreakdown
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0)
        .mockResolvedValueOnce(0);

      const result = await MetricsService.getWeeklyMetrics(userId);

      expect(result.summary.totalTasksCompleted).toBe(10);
      expect(result.summary.peakProductivityDay).toBeDefined();
    });

    it('devrait utiliser une date personnalisée si fournie', async () => {
      const customDate = new Date('2024-02-01');

      // Mock minimal pour éviter les erreurs
      vi.mocked(prisma.task.count).mockResolvedValue(0);
      vi.mocked(prisma.task.groupBy).mockResolvedValue([]);

      await MetricsService.getWeeklyMetrics(userId, customDate);

      expect(prisma.task.count).toHaveBeenCalled();
    });
  });

  describe('getVelocityTrends', () => {
    it('devrait retourner les tendances de vélocité', async () => {
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(5) // completed semaine 1
        .mockResolvedValueOnce(3) // created semaine 1
        .mockResolvedValueOnce(7) // completed semaine 2
        .mockResolvedValueOnce(4) // created semaine 2
        .mockResolvedValueOnce(6) // completed semaine 3
        .mockResolvedValueOnce(5) // created semaine 3
        .mockResolvedValueOnce(8) // completed semaine 4
        .mockResolvedValueOnce(6); // created semaine 4

      const result = await MetricsService.getVelocityTrends(userId, 4);

      expect(result).toHaveLength(4);
      expect(result[0]).toHaveProperty('date');
      expect(result[0]).toHaveProperty('completed');
      expect(result[0]).toHaveProperty('created');
      expect(result[0]).toHaveProperty('velocity');
    });

    it('devrait calculer correctement la vélocité', async () => {
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(10) // completed
        .mockResolvedValueOnce(5); // created

      const result = await MetricsService.getVelocityTrends(userId, 1);

      expect(result[0].velocity).toBe(200); // 10/5 * 100
    });

    it("devrait gérer le cas où aucune tâche n'est créée", async () => {
      vi.mocked(prisma.task.count)
        .mockResolvedValueOnce(5) // completed
        .mockResolvedValueOnce(0); // created

      const result = await MetricsService.getVelocityTrends(userId, 1);

      expect(result[0].velocity).toBe(0);
    });

    it('devrait utiliser le nombre de semaines personnalisé', async () => {
      vi.mocked(prisma.task.count).mockResolvedValue(0);

      const result = await MetricsService.getVelocityTrends(userId, 8);

      expect(result).toHaveLength(8);
    });
  });
});
