/**
 * Tests unitaires pour JiraAnalyticsCacheService
 */

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { jiraAnalyticsCache } from '../analytics-cache';
import { JiraAnalytics } from '@/lib/types';

describe('JiraAnalyticsCacheService', () => {
  const mockConfig = {
    baseUrl: 'https://test.atlassian.net',
    email: 'test@example.com',
    apiToken: 'token123',
    projectKey: 'TEST',
  };

  const mockAnalytics: JiraAnalytics = {
    project: {
      key: 'TEST',
      name: 'Test Project',
      totalIssues: 10,
    },
    teamMetrics: {
      totalAssignees: 5,
      activeAssignees: 5,
      issuesDistribution: [],
    },
    velocityMetrics: {
      currentSprintPoints: 0,
      averageVelocity: 0,
      sprintHistory: [],
    },
    cycleTimeMetrics: {
      averageCycleTime: 0,
      cycleTimeByType: [],
    },
    workInProgress: {
      byStatus: [],
      byAssignee: [],
    },
  };

  beforeEach(() => {
    jiraAnalyticsCache.invalidateAll();
    vi.useFakeTimers();
  });

  afterEach(() => {
    jiraAnalyticsCache.invalidateAll();
    jiraAnalyticsCache.stopCleanupInterval();
    vi.useRealTimers();
  });

  describe('set et get', () => {
    it('devrait stocker et récupérer les analytics', () => {
      jiraAnalyticsCache.set(mockConfig, mockAnalytics);

      const result = jiraAnalyticsCache.get(mockConfig);

      expect(result).toEqual(mockAnalytics);
    });

    it('devrait retourner null si pas de cache', () => {
      const result = jiraAnalyticsCache.get(mockConfig);

      expect(result).toBeNull();
    });

    it('devrait utiliser un TTL personnalisé', () => {
      const customTTL = 60 * 60 * 1000; // 1 heure
      jiraAnalyticsCache.set(mockConfig, mockAnalytics, customTTL);

      const result = jiraAnalyticsCache.get(mockConfig);
      expect(result).toEqual(mockAnalytics);
    });
  });

  describe('invalidate', () => {
    it('devrait invalider le cache pour un projet spécifique', () => {
      jiraAnalyticsCache.set(mockConfig, mockAnalytics);

      jiraAnalyticsCache.invalidate(mockConfig);

      const result = jiraAnalyticsCache.get(mockConfig);
      expect(result).toBeNull();
    });

    it('devrait invalider tout le cache', () => {
      jiraAnalyticsCache.set(mockConfig, mockAnalytics);

      jiraAnalyticsCache.invalidateAll();

      const result = jiraAnalyticsCache.get(mockConfig);
      expect(result).toBeNull();
    });
  });

  describe('expiration', () => {
    it('devrait expirer après le TTL', () => {
      const shortTTL = 1000; // 1 seconde
      jiraAnalyticsCache.set(mockConfig, mockAnalytics, shortTTL);

      // Avancer le temps de 2 secondes
      vi.advanceTimersByTime(2000);

      const result = jiraAnalyticsCache.get(mockConfig);
      expect(result).toBeNull();
    });

    it('ne devrait pas expirer avant le TTL', () => {
      const shortTTL = 5000; // 5 secondes
      jiraAnalyticsCache.set(mockConfig, mockAnalytics, shortTTL);

      // Avancer le temps de 2 secondes seulement
      vi.advanceTimersByTime(2000);

      const result = jiraAnalyticsCache.get(mockConfig);
      expect(result).toEqual(mockAnalytics);
    });
  });

  describe('config hash', () => {
    it('devrait invalider si la config change', () => {
      jiraAnalyticsCache.set(mockConfig, mockAnalytics);

      const newConfig = {
        ...mockConfig,
        apiToken: 'newtoken',
      };

      const result = jiraAnalyticsCache.get(newConfig);
      expect(result).toBeNull();
    });

    it('devrait garder le cache si la config est identique', () => {
      jiraAnalyticsCache.set(mockConfig, mockAnalytics);

      const sameConfig = { ...mockConfig };
      const result = jiraAnalyticsCache.get(sameConfig);

      expect(result).toEqual(mockAnalytics);
    });
  });

  describe('has', () => {
    it('devrait retourner true si le cache existe', () => {
      jiraAnalyticsCache.set(mockConfig, mockAnalytics);

      const exists = jiraAnalyticsCache.has(mockConfig);

      expect(exists).toBe(true);
    });

    it("devrait retourner false si le cache n'existe pas", () => {
      const exists = jiraAnalyticsCache.has(mockConfig);

      expect(exists).toBe(false);
    });
  });

  describe('getStats', () => {
    it('devrait retourner les statistiques du cache', () => {
      jiraAnalyticsCache.set(mockConfig, mockAnalytics);

      const stats = jiraAnalyticsCache.getStats();

      expect(stats.totalEntries).toBe(1);
      expect(stats.projects).toHaveLength(1);
      expect(stats.projects[0].projectKey).toBe('TEST');
    });

    it('devrait retourner des stats vides si pas de cache', () => {
      const stats = jiraAnalyticsCache.getStats();

      expect(stats.totalEntries).toBe(0);
      expect(stats.projects).toHaveLength(0);
    });
  });

  describe('forceCleanup', () => {
    it('devrait nettoyer les entrées expirées', () => {
      const shortTTL = 1000; // 1 seconde
      jiraAnalyticsCache.set(mockConfig, mockAnalytics, shortTTL);

      // Avancer le temps pour expirer
      vi.advanceTimersByTime(2000);

      const cleanedCount = jiraAnalyticsCache.forceCleanup();

      expect(cleanedCount).toBe(1);
      expect(jiraAnalyticsCache.get(mockConfig)).toBeNull();
    });

    it('devrait retourner 0 si aucune entrée expirée', () => {
      jiraAnalyticsCache.set(mockConfig, mockAnalytics);

      const cleanedCount = jiraAnalyticsCache.forceCleanup();

      expect(cleanedCount).toBe(0);
      expect(jiraAnalyticsCache.get(mockConfig)).not.toBeNull();
    });
  });

  describe('multiple projects', () => {
    it('devrait gérer plusieurs projets indépendamment', () => {
      const config1 = { ...mockConfig, projectKey: 'PROJ1' };
      const config2 = { ...mockConfig, projectKey: 'PROJ2' };

      jiraAnalyticsCache.set(config1, mockAnalytics);
      jiraAnalyticsCache.set(config2, mockAnalytics);

      expect(jiraAnalyticsCache.get(config1)).toEqual(mockAnalytics);
      expect(jiraAnalyticsCache.get(config2)).toEqual(mockAnalytics);

      jiraAnalyticsCache.invalidate(config1);

      expect(jiraAnalyticsCache.get(config1)).toBeNull();
      expect(jiraAnalyticsCache.get(config2)).toEqual(mockAnalytics);
    });
  });
});
