/**
 * Tests unitaires pour TagAnalyticsService
 */

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

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

describe('TagAnalyticsService', () => {
  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('getTagDistributionMetrics', () => {
    it('devrait calculer la distribution des tags', async () => {
      const mockTasks = [
        {
          id: '1',
          title: 'Tâche avec tag',
          description: 'Description',
          status: 'done' as TaskStatus,
          priority: 'high' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-10'),
          updatedAt: new Date('2024-01-12'),
          completedAt: new Date('2024-01-12'),
          dueDate: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'important',
                color: '#ff0000',
              },
            },
          ],
        },
        {
          id: '2',
          title: 'Autre tâche',
          description: 'Description',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-11'),
          updatedAt: new Date('2024-01-11'),
          completedAt: null,
          dueDate: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'important',
                color: '#ff0000',
              },
            },
            {
              tag: {
                name: 'urgent',
                color: '#00ff00',
              },
            },
          ],
        },
      ];

      const mockTags = [
        { name: 'important', color: '#ff0000' },
        { name: 'urgent', color: '#00ff00' },
      ];

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

      const result =
        await TagAnalyticsService.getTagDistributionMetrics(userId);

      expect(result).toHaveProperty('tagDistribution');
      expect(result).toHaveProperty('topTags');
      expect(result).toHaveProperty('tagTrends');
      expect(result).toHaveProperty('tagStats');

      expect(result.tagDistribution.length).toBeGreaterThan(0);
      const importantTag = result.tagDistribution.find(
        (t) => t.tagName === 'important'
      );
      expect(importantTag).toBeDefined();
      expect(importantTag?.count).toBe(2);
      expect(importantTag?.percentage).toBe(100); // 2 tâches sur 2
    });

    it('devrait calculer les top tags avec métriques', async () => {
      const mockTasks = [
        {
          id: '1',
          title: 'Tâche complétée',
          status: 'done' as TaskStatus,
          priority: 'high' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-10'),
          updatedAt: new Date('2024-01-12'),
          completedAt: new Date('2024-01-12'),
          dueDate: null,
          description: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'important',
                color: '#ff0000',
              },
            },
          ],
        },
        {
          id: '2',
          title: 'Tâche non complétée',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-11'),
          updatedAt: new Date('2024-01-11'),
          completedAt: null,
          dueDate: null,
          description: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'important',
                color: '#ff0000',
              },
            },
          ],
        },
      ];

      const mockTags = [{ name: 'important', color: '#ff0000' }];

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

      const result =
        await TagAnalyticsService.getTagDistributionMetrics(userId);

      const importantTag = result.topTags.find(
        (t) => t.tagName === 'important'
      );
      expect(importantTag).toBeDefined();
      expect(importantTag?.usage).toBe(2);
      expect(importantTag?.completionRate).toBe(50); // 1 complétée sur 2
    });

    it('devrait calculer les tendances des tags', async () => {
      const mockTasks = [
        {
          id: '1',
          title: 'Tâche',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-10'),
          updatedAt: new Date('2024-01-10'),
          completedAt: null,
          dueDate: null,
          description: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'important',
                color: '#ff0000',
              },
            },
          ],
        },
      ];

      const mockTags = [{ name: 'important', color: '#ff0000' }];

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

      const result =
        await TagAnalyticsService.getTagDistributionMetrics(userId);

      expect(result.tagTrends.length).toBeGreaterThan(0);
      const trend = result.tagTrends.find((t) => t.tagName === 'important');
      expect(trend).toBeDefined();
      expect(trend?.count).toBe(1);
    });

    it('devrait calculer les statistiques des tags', async () => {
      const mockTasks = [
        {
          id: '1',
          title: 'Tâche 1',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-10'),
          updatedAt: new Date('2024-01-10'),
          completedAt: null,
          dueDate: null,
          description: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'important',
                color: '#ff0000',
              },
            },
          ],
        },
        {
          id: '2',
          title: 'Tâche 2',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-11'),
          updatedAt: new Date('2024-01-11'),
          completedAt: null,
          dueDate: null,
          description: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'important',
                color: '#ff0000',
              },
            },
            {
              tag: {
                name: 'urgent',
                color: '#00ff00',
              },
            },
          ],
        },
      ];

      const mockTags = [
        { name: 'important', color: '#ff0000' },
        { name: 'urgent', color: '#00ff00' },
      ];

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

      const result =
        await TagAnalyticsService.getTagDistributionMetrics(userId);

      expect(result.tagStats.totalTags).toBe(2);
      expect(result.tagStats.activeTags).toBe(2);
      expect(result.tagStats.mostUsedTag).toBe('important');
      expect(result.tagStats.avgTasksPerTag).toBeGreaterThan(0);
    });

    it('devrait filtrer par sources si spécifié', async () => {
      const mockTasks = [
        {
          id: '1',
          title: 'Tâche Jira',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'jira' as TaskSource,
          sourceId: 'JIRA-1',
          ownerId: userId,
          createdAt: new Date('2024-01-10'),
          updatedAt: new Date('2024-01-10'),
          completedAt: null,
          dueDate: null,
          description: null,
          jiraProject: 'PROJ',
          jiraKey: 'JIRA-1',
          jiraType: 'Task',
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'important',
                color: '#ff0000',
              },
            },
          ],
        },
        {
          id: '2',
          title: 'Tâche Manual',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-11'),
          updatedAt: new Date('2024-01-11'),
          completedAt: null,
          dueDate: null,
          description: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'urgent',
                color: '#00ff00',
              },
            },
          ],
        },
      ];

      const mockTags = [
        { name: 'important', color: '#ff0000' },
        { name: 'urgent', color: '#00ff00' },
      ];

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

      const result = await TagAnalyticsService.getTagDistributionMetrics(
        userId,
        undefined,
        ['jira']
      );

      const tagNames = result.tagDistribution.map((t) => t.tagName);
      expect(tagNames).toContain('important');
      expect(tagNames).not.toContain('urgent');
    });

    it('devrait utiliser une période personnalisée si fournie', async () => {
      const startDate = new Date('2024-01-01');
      const endDate = new Date('2024-01-10');

      vi.mocked(prisma.task.findMany).mockResolvedValue([]);
      vi.mocked(prisma.tag.findMany).mockResolvedValue([]);

      await TagAnalyticsService.getTagDistributionMetrics(userId, {
        start: startDate,
        end: endDate,
      });

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

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

      await expect(
        TagAnalyticsService.getTagDistributionMetrics(userId)
      ).rejects.toThrow(
        'Impossible de calculer les métriques de distribution par tags'
      );
    });

    it('devrait trier les tags par utilisation décroissante', async () => {
      const mockTasks = [
        {
          id: '1',
          title: 'Tâche',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-10'),
          updatedAt: new Date('2024-01-10'),
          completedAt: null,
          dueDate: null,
          description: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'frequent',
                color: '#0000ff',
              },
            },
          ],
        },
        {
          id: '2',
          title: 'Tâche',
          status: 'todo' as TaskStatus,
          priority: 'medium' as TaskPriority,
          source: 'manual' as TaskSource,
          sourceId: null,
          ownerId: userId,
          createdAt: new Date('2024-01-11'),
          updatedAt: new Date('2024-01-11'),
          completedAt: null,
          dueDate: null,
          description: null,
          jiraProject: null,
          jiraKey: null,
          jiraType: null,
          assignee: null,
          taskTags: [
            {
              tag: {
                name: 'frequent',
                color: '#0000ff',
              },
            },
            {
              tag: {
                name: 'rare',
                color: '#ffff00',
              },
            },
          ],
        },
      ];

      const mockTags = [
        { name: 'frequent', color: '#0000ff' },
        { name: 'rare', color: '#ffff00' },
      ];

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

      const result =
        await TagAnalyticsService.getTagDistributionMetrics(userId);

      expect(result.tagDistribution[0].tagName).toBe('frequent');
      expect(result.tagDistribution[0].count).toBeGreaterThan(
        result.tagDistribution[1]?.count || 0
      );
    });
  });
});
