/**
 * Tests unitaires pour DeadlineAnalyticsService
 */

/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { DeadlineAnalyticsService } from '../deadline-analytics';
import { TaskStatus } from '@/lib/types';
import { prisma } from '@/services/core/database';

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

describe('DeadlineAnalyticsService', () => {
  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('getDeadlineMetrics', () => {
    it('devrait catégoriser les tâches par urgence', async () => {
      const overdueDate = new Date('2024-01-10'); // 5 jours en retard
      const criticalDate = new Date('2024-01-16'); // 1 jour restant
      const warningDate = new Date('2024-01-20'); // 5 jours restants
      const upcomingDate = new Date('2024-01-25'); // 10 jours restants

      const mockTasks = [
        {
          id: '1',
          title: 'Tâche en retard',
          status: 'todo' as TaskStatus,
          priority: 'high',
          dueDate: overdueDate,
          source: 'manual',
          ownerId: userId,
          taskTags: [],
        },
        {
          id: '2',
          title: 'Tâche critique',
          status: 'in_progress' as TaskStatus,
          priority: 'urgent',
          dueDate: criticalDate,
          source: 'jira',
          ownerId: userId,
          taskTags: [],
        },
        {
          id: '3',
          title: 'Tâche warning',
          status: 'todo' as TaskStatus,
          priority: 'medium',
          dueDate: warningDate,
          source: 'manual',
          ownerId: userId,
          taskTags: [],
        },
        {
          id: '4',
          title: 'Tâche à venir',
          status: 'todo' as TaskStatus,
          priority: 'low',
          dueDate: upcomingDate,
          source: 'manual',
          ownerId: userId,
          taskTags: [],
        },
      ];

      vi.mocked(prisma.task.findMany).mockResolvedValue(mockTasks as any);

      const result = await DeadlineAnalyticsService.getDeadlineMetrics(userId);

      expect(result.overdue).toHaveLength(1);
      expect(result.overdue[0].id).toBe('1');
      expect(result.critical).toHaveLength(1);
      expect(result.critical[0].id).toBe('2');
      expect(result.warning).toHaveLength(1);
      expect(result.warning[0].id).toBe('3');
      expect(result.upcoming).toHaveLength(1);
      expect(result.upcoming[0].id).toBe('4');

      expect(result.summary.overdueCount).toBe(1);
      expect(result.summary.criticalCount).toBe(1);
      expect(result.summary.warningCount).toBe(1);
      expect(result.summary.upcomingCount).toBe(1);
      expect(result.summary.totalWithDeadlines).toBe(4);
    });

    it('devrait exclure les tâches terminées ou annulées', async () => {
      // Le service filtre déjà les tâches terminées dans la requête Prisma
      // donc le mock devrait retourner une liste vide
      vi.mocked(prisma.task.findMany).mockResolvedValue([]);

      const result = await DeadlineAnalyticsService.getDeadlineMetrics(userId);

      expect(result.summary.totalWithDeadlines).toBe(0);
    });

    it('devrait filtrer par sources si spécifié', async () => {
      const mockTasks = [
        {
          id: '1',
          title: 'Tâche Jira',
          status: 'todo' as TaskStatus,
          priority: 'high',
          dueDate: new Date('2024-01-16'),
          source: 'jira',
          ownerId: userId,
          taskTags: [],
        },
        {
          id: '2',
          title: 'Tâche Manual',
          status: 'todo' as TaskStatus,
          priority: 'medium',
          dueDate: new Date('2024-01-17'),
          source: 'manual',
          ownerId: userId,
          taskTags: [],
        },
      ];

      vi.mocked(prisma.task.findMany).mockResolvedValue(mockTasks as any);

      const result = await DeadlineAnalyticsService.getDeadlineMetrics(userId, [
        'jira',
      ]);

      const allTaskIds = [
        ...result.overdue,
        ...result.critical,
        ...result.warning,
        ...result.upcoming,
      ].map((t) => t.id);

      expect(allTaskIds).toContain('1');
      expect(allTaskIds).not.toContain('2');
    });

    it('devrait calculer correctement les jours restants', async () => {
      // Utiliser une date normalisée pour éviter les problèmes de timezone
      // La date mockée est le 15 janvier à 12:00, donc le 20 janvier à 00:00
      // donne environ 4-5 jours selon le calcul avec Math.ceil
      const dueDate = new Date('2024-01-20T00:00:00Z'); // 5 jours dans le futur
      const mockTasks = [
        {
          id: '1',
          title: 'Tâche test',
          status: 'todo' as TaskStatus,
          priority: 'medium',
          dueDate,
          source: 'manual',
          ownerId: userId,
          taskTags: [],
        },
      ];

      vi.mocked(prisma.task.findMany).mockResolvedValue(mockTasks as any);

      const result = await DeadlineAnalyticsService.getDeadlineMetrics(userId);

      // Le calcul utilise Math.ceil, donc avec les heures ça peut donner 5 ou 6 jours
      // Vérifions que c'est dans la plage warning (3-7 jours)
      expect(result.warning[0].daysRemaining).toBeGreaterThanOrEqual(5);
      expect(result.warning[0].daysRemaining).toBeLessThanOrEqual(6);
      expect(result.warning[0].urgencyLevel).toBe('warning');
    });

    it('devrait gérer les erreurs de base de données', async () => {
      vi.mocked(prisma.task.findMany).mockRejectedValue(
        new Error('Database error')
      );

      await expect(
        DeadlineAnalyticsService.getDeadlineMetrics(userId)
      ).rejects.toThrow("Impossible d'analyser les échéances");
    });
  });

  describe('getCriticalDeadlines', () => {
    it('devrait retourner les 10 tâches les plus critiques', async () => {
      const mockTasks = Array.from({ length: 15 }, (_, i) => ({
        id: `${i + 1}`,
        title: `Tâche ${i + 1}`,
        status: 'todo' as TaskStatus,
        priority: 'high',
        dueDate: new Date(`2024-01-${10 + i}`), // Dates en retard
        source: 'manual',
        ownerId: userId,
        taskTags: [],
      }));

      vi.mocked(prisma.task.findMany).mockResolvedValue(mockTasks as any);

      const result =
        await DeadlineAnalyticsService.getCriticalDeadlines(userId);

      expect(result.length).toBeLessThanOrEqual(10);
    });
  });

  describe('analyzeImpactByPriority', () => {
    it("devrait analyser l'impact par priorité", () => {
      const tasks = [
        {
          id: '1',
          title: 'Tâche haute priorité en retard',
          status: 'todo' as TaskStatus,
          priority: 'high',
          dueDate: new Date('2024-01-10'),
          daysRemaining: -5,
          urgencyLevel: 'overdue' as const,
          source: 'manual',
          tags: [],
        },
        {
          id: '2',
          title: 'Tâche haute priorité critique',
          status: 'todo' as TaskStatus,
          priority: 'high',
          dueDate: new Date('2024-01-16'),
          daysRemaining: 1,
          urgencyLevel: 'critical' as const,
          source: 'manual',
          tags: [],
        },
        {
          id: '3',
          title: 'Tâche moyenne priorité',
          status: 'todo' as TaskStatus,
          priority: 'medium',
          dueDate: new Date('2024-01-20'),
          daysRemaining: 5,
          urgencyLevel: 'warning' as const,
          source: 'manual',
          tags: [],
        },
      ];

      const result = DeadlineAnalyticsService.analyzeImpactByPriority(tasks);

      expect(result.length).toBeGreaterThan(0);
      const highPriority = result.find((p) => p.priority === 'high');
      expect(highPriority).toBeDefined();
      expect(highPriority?.overdueCount).toBe(1);
      expect(highPriority?.criticalCount).toBe(1);
    });

    it('devrait trier par impact décroissant', () => {
      const tasks = [
        {
          id: '1',
          title: 'Tâche haute priorité',
          status: 'todo' as TaskStatus,
          priority: 'high',
          dueDate: new Date('2024-01-10'),
          daysRemaining: -5,
          urgencyLevel: 'overdue' as const,
          source: 'manual',
          tags: [],
        },
        {
          id: '2',
          title: 'Tâche basse priorité',
          status: 'todo' as TaskStatus,
          priority: 'low',
          dueDate: new Date('2024-01-20'),
          daysRemaining: 5,
          urgencyLevel: 'warning' as const,
          source: 'manual',
          tags: [],
        },
      ];

      const result = DeadlineAnalyticsService.analyzeImpactByPriority(tasks);

      expect(result[0].priority).toBe('high'); // Plus d'impact
    });
  });

  describe('calculateRiskMetrics', () => {
    it('devrait calculer un score de risque faible', () => {
      const metrics = {
        overdue: [],
        critical: [],
        warning: [],
        upcoming: [],
        summary: {
          overdueCount: 0,
          criticalCount: 0,
          warningCount: 1,
          upcomingCount: 2,
          totalWithDeadlines: 3,
        },
      };

      const result = DeadlineAnalyticsService.calculateRiskMetrics(metrics);

      expect(result.riskScore).toBeLessThan(25);
      expect(result.riskLevel).toBe('low');
    });

    it('devrait calculer un score de risque critique', () => {
      const metrics = {
        overdue: [],
        critical: [],
        warning: [],
        upcoming: [],
        summary: {
          overdueCount: 3,
          criticalCount: 2,
          warningCount: 0,
          upcomingCount: 0,
          totalWithDeadlines: 5,
        },
      };

      const result = DeadlineAnalyticsService.calculateRiskMetrics(metrics);

      expect(result.riskScore).toBeGreaterThanOrEqual(75);
      expect(result.riskLevel).toBe('critical');
    });

    it('devrait limiter le score à 100', () => {
      const metrics = {
        overdue: [],
        critical: [],
        warning: [],
        upcoming: [],
        summary: {
          overdueCount: 10,
          criticalCount: 10,
          warningCount: 10,
          upcomingCount: 10,
          totalWithDeadlines: 40,
        },
      };

      const result = DeadlineAnalyticsService.calculateRiskMetrics(metrics);

      expect(result.riskScore).toBeLessThanOrEqual(100);
    });

    it('devrait fournir des recommandations appropriées', () => {
      const metrics = {
        overdue: [],
        critical: [],
        warning: [],
        upcoming: [],
        summary: {
          overdueCount: 2,
          criticalCount: 1,
          warningCount: 2,
          upcomingCount: 1,
          totalWithDeadlines: 6,
        },
      };

      const result = DeadlineAnalyticsService.calculateRiskMetrics(metrics);

      expect(result.recommendation).toBeDefined();
      expect(result.recommendation.length).toBeGreaterThan(0);
    });
  });
});
