/**
 * Tests unitaires pour SystemInfoService
 */

/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { SystemInfoService } from '../system-info';
import { prisma } from '../database';
import { readFile } from 'fs/promises';
import { stat } from 'fs/promises';

// Mock de prisma
vi.mock('../database', () => ({
  prisma: {
    task: {
      count: vi.fn(),
    },
    userPreferences: {
      count: vi.fn(),
    },
    tag: {
      count: vi.fn(),
    },
    dailyCheckbox: {
      count: vi.fn(),
    },
  },
}));

// Mock de fs/promises
vi.mock('fs/promises', () => ({
  readFile: vi.fn(),
  stat: vi.fn(),
}));

// Mock de path
vi.mock('path', () => ({
  join: (...args: string[]) => args.join('/'),
  resolve: (...args: string[]) => args.join('/'),
}));

// Mock du service backup
vi.mock('@/services/data-management/backup', () => ({
  backupService: {
    listBackups: vi.fn(),
  },
}));

describe('SystemInfoService', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.stubEnv('NODE_ENV', 'test');
  });

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

  describe('getSystemInfo', () => {
    it('devrait retourner les informations système complètes', async () => {
      const mockPackageJson = {
        name: 'towercontrol',
        version: '1.0.0',
      };

      vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
      vi.mocked(prisma.task.count).mockResolvedValue(10);
      vi.mocked(prisma.userPreferences.count).mockResolvedValue(1);
      vi.mocked(prisma.tag.count).mockResolvedValue(5);
      vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(20);

      const { backupService } = await import(
        '@/services/data-management/backup'
      );
      vi.mocked(backupService.listBackups).mockResolvedValue([
        { id: '1', filename: 'backup1.db.gz' },
        { id: '2', filename: 'backup2.db.gz' },
      ] as any);

      vi.mocked(stat).mockResolvedValue({
        size: 1024 * 1024, // 1 MB
      } as any);

      const result = await SystemInfoService.getSystemInfo();

      expect(result).toHaveProperty('version');
      expect(result).toHaveProperty('environment');
      expect(result).toHaveProperty('database');
      expect(result).toHaveProperty('backups');
      expect(result).toHaveProperty('app');
      expect(result).toHaveProperty('uptime');
      expect(result).toHaveProperty('lastUpdate');

      expect(result.version).toBe('1.0.0');
      expect(result.environment).toBe('test');
      expect(result.database.totalTasks).toBe(10);
      expect(result.database.totalUsers).toBe(1);
      expect(result.database.totalBackups).toBe(2);
      expect(result.database.totalTags).toBe(5);
      expect(result.database.totalDailies).toBe(20);
    });

    it('devrait gérer les erreurs lors de la lecture du package.json', async () => {
      vi.mocked(readFile).mockRejectedValue(new Error('File not found'));

      vi.mocked(prisma.task.count).mockResolvedValue(0);
      vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
      vi.mocked(prisma.tag.count).mockResolvedValue(0);
      vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);

      const { backupService } = await import(
        '@/services/data-management/backup'
      );
      vi.mocked(backupService.listBackups).mockResolvedValue([]);

      vi.mocked(stat).mockRejectedValue(new Error('File not found'));

      const result = await SystemInfoService.getSystemInfo();

      expect(result.version).toBe('1.0.0'); // Valeur par défaut
      expect(result.database.databaseSize).toBe('N/A');
    });

    it('devrait calculer correctement la taille de la base de données', async () => {
      const mockPackageJson = {
        name: 'towercontrol',
        version: '1.0.0',
      };

      vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
      vi.mocked(prisma.task.count).mockResolvedValue(0);
      vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
      vi.mocked(prisma.tag.count).mockResolvedValue(0);
      vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);

      const { backupService } = await import(
        '@/services/data-management/backup'
      );
      vi.mocked(backupService.listBackups).mockResolvedValue([]);

      // Test différentes tailles
      vi.mocked(stat).mockResolvedValue({
        size: 1024, // 1 KB
      } as any);

      const result = await SystemInfoService.getSystemInfo();

      expect(result.database.databaseSize).toContain('KB');
    });

    it('devrait utiliser DATABASE_URL pour trouver la base de données', async () => {
      vi.stubEnv('DATABASE_URL', 'file:/path/to/db.db');

      const mockPackageJson = {
        name: 'towercontrol',
        version: '1.0.0',
      };

      vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
      vi.mocked(prisma.task.count).mockResolvedValue(0);
      vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
      vi.mocked(prisma.tag.count).mockResolvedValue(0);
      vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);

      const { backupService } = await import(
        '@/services/data-management/backup'
      );
      vi.mocked(backupService.listBackups).mockResolvedValue([]);

      vi.mocked(stat).mockResolvedValue({
        size: 1024,
      } as any);

      await SystemInfoService.getSystemInfo();

      expect(stat).toHaveBeenCalled();
    });

    it('devrait utiliser BACKUP_DATABASE_PATH si défini', async () => {
      vi.stubEnv('BACKUP_DATABASE_PATH', '/custom/path/db.db');

      const mockPackageJson = {
        name: 'towercontrol',
        version: '1.0.0',
      };

      vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
      vi.mocked(prisma.task.count).mockResolvedValue(0);
      vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
      vi.mocked(prisma.tag.count).mockResolvedValue(0);
      vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);

      const { backupService } = await import(
        '@/services/data-management/backup'
      );
      vi.mocked(backupService.listBackups).mockResolvedValue([]);

      vi.mocked(stat).mockResolvedValue({
        size: 1024,
      } as any);

      await SystemInfoService.getSystemInfo();

      expect(stat).toHaveBeenCalled();
    });

    it('devrait gérer les erreurs de base de données', async () => {
      const mockPackageJson = {
        name: 'towercontrol',
        version: '1.0.0',
      };

      vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
      vi.mocked(prisma.task.count).mockRejectedValue(
        new Error('Database error')
      );

      const { backupService } = await import(
        '@/services/data-management/backup'
      );
      vi.mocked(backupService.listBackups).mockResolvedValue([]);

      const result = await SystemInfoService.getSystemInfo();

      expect(result.database.totalTasks).toBe(0);
      expect(result.database.totalUsers).toBe(0);
    });

    it("devrait calculer correctement l'uptime", async () => {
      const mockPackageJson = {
        name: 'towercontrol',
        version: '1.0.0',
      };

      vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
      vi.mocked(prisma.task.count).mockResolvedValue(0);
      vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
      vi.mocked(prisma.tag.count).mockResolvedValue(0);
      vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);

      const { backupService } = await import(
        '@/services/data-management/backup'
      );
      vi.mocked(backupService.listBackups).mockResolvedValue([]);

      vi.mocked(stat).mockResolvedValue({
        size: 1024,
      } as any);

      const result = await SystemInfoService.getSystemInfo();

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