/**
 * Tests unitaires pour usersService
 */

/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { usersService } from '../users';
import { prisma } from '../core/database';
import bcrypt from 'bcryptjs';

// Mock de prisma
vi.mock('../core/database', () => ({
  prisma: {
    user: {
      create: vi.fn(),
      findUnique: vi.fn(),
      update: vi.fn(),
    },
  },
}));

// Mock de bcrypt
vi.mock('bcryptjs', () => ({
  default: {
    hash: vi.fn(),
    compare: vi.fn(),
  },
}));

describe('usersService', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('createUser', () => {
    it('devrait créer un nouvel utilisateur', async () => {
      const mockUser = {
        id: 'user-1',
        email: 'test@example.com',
        name: 'Test User',
        firstName: 'Test',
        lastName: 'User',
        avatar: null,
        role: 'user',
        isActive: true,
        lastLoginAt: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(bcrypt.hash).mockResolvedValue('hashedPassword' as any);
      vi.mocked(prisma.user.create).mockResolvedValue(mockUser as any);

      const user = await usersService.createUser({
        email: 'test@example.com',
        name: 'Test User',
        firstName: 'Test',
        lastName: 'User',
        password: 'password123',
      });

      expect(user).toEqual(mockUser);
      expect(bcrypt.hash).toHaveBeenCalledWith('password123', 12);
      expect(prisma.user.create).toHaveBeenCalledWith({
        data: {
          email: 'test@example.com',
          name: 'Test User',
          firstName: 'Test',
          lastName: 'User',
          avatar: undefined,
          role: 'user',
          password: 'hashedPassword',
        },
        select: expect.any(Object),
      });
    });

    it('devrait utiliser le rôle par défaut si non spécifié', async () => {
      const mockUser = {
        id: 'user-1',
        email: 'test@example.com',
        name: null,
        firstName: null,
        lastName: null,
        avatar: null,
        role: 'user',
        isActive: true,
        lastLoginAt: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(bcrypt.hash).mockResolvedValue('hashedPassword' as any);
      vi.mocked(prisma.user.create).mockResolvedValue(mockUser as any);

      await usersService.createUser({
        email: 'test@example.com',
        password: 'password123',
      });

      expect(prisma.user.create).toHaveBeenCalledWith(
        expect.objectContaining({
          data: expect.objectContaining({
            role: 'user',
          }),
        })
      );
    });
  });

  describe('getUserByEmail', () => {
    it('devrait récupérer un utilisateur par email', async () => {
      const mockUser = {
        id: 'user-1',
        email: 'test@example.com',
        name: 'Test User',
        firstName: 'Test',
        lastName: 'User',
        avatar: null,
        role: 'user',
        isActive: true,
        lastLoginAt: null,
        password: 'hashedPassword',
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(prisma.user.findUnique).mockResolvedValue(mockUser as any);

      const user = await usersService.getUserByEmail('test@example.com');

      expect(user).toEqual(mockUser);
      expect(prisma.user.findUnique).toHaveBeenCalledWith({
        where: { email: 'test@example.com' },
        select: expect.any(Object),
      });
    });

    it("devrait retourner null si l'utilisateur n'existe pas", async () => {
      vi.mocked(prisma.user.findUnique).mockResolvedValue(null);

      const user = await usersService.getUserByEmail('nonexistent@example.com');

      expect(user).toBeNull();
    });
  });

  describe('getUserById', () => {
    it('devrait récupérer un utilisateur par ID', async () => {
      const mockUser = {
        id: 'user-1',
        email: 'test@example.com',
        name: 'Test User',
        firstName: 'Test',
        lastName: 'User',
        avatar: null,
        role: 'user',
        isActive: true,
        lastLoginAt: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(prisma.user.findUnique).mockResolvedValue(mockUser as any);

      const user = await usersService.getUserById('user-1');

      expect(user).toEqual(mockUser);
      expect(prisma.user.findUnique).toHaveBeenCalledWith({
        where: { id: 'user-1' },
        select: expect.any(Object),
      });
    });
  });

  describe('verifyPassword', () => {
    it('devrait vérifier un mot de passe correct', async () => {
      vi.mocked(bcrypt.compare).mockResolvedValue(true as any);

      const isValid = await usersService.verifyPassword(
        'password123',
        'hashedPassword'
      );

      expect(isValid).toBe(true);
      expect(bcrypt.compare).toHaveBeenCalledWith(
        'password123',
        'hashedPassword'
      );
    });

    it('devrait retourner false pour un mot de passe incorrect', async () => {
      vi.mocked(bcrypt.compare).mockResolvedValue(false as any);

      const isValid = await usersService.verifyPassword(
        'wrongPassword',
        'hashedPassword'
      );

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

  describe('emailExists', () => {
    it("devrait retourner true si l'email existe", async () => {
      vi.mocked(prisma.user.findUnique).mockResolvedValue({
        id: 'user-1',
        email: 'test@example.com',
      } as any);

      const exists = await usersService.emailExists('test@example.com');

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

    it("devrait retourner false si l'email n'existe pas", async () => {
      vi.mocked(prisma.user.findUnique).mockResolvedValue(null);

      const exists = await usersService.emailExists('nonexistent@example.com');

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

  describe('updateLastLogin', () => {
    it('devrait mettre à jour la date de dernière connexion', async () => {
      vi.mocked(prisma.user.update).mockResolvedValue({} as any);

      await usersService.updateLastLogin('user-1');

      expect(prisma.user.update).toHaveBeenCalledWith({
        where: { id: 'user-1' },
        data: {
          lastLoginAt: expect.any(Date),
        },
      });
    });
  });

  describe('updateUser', () => {
    it('devrait mettre à jour un utilisateur', async () => {
      const mockUser = {
        id: 'user-1',
        email: 'test@example.com',
        name: 'Updated Name',
        firstName: 'Updated',
        lastName: 'Name',
        avatar: null,
        role: 'user',
        isActive: true,
        lastLoginAt: null,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      vi.mocked(prisma.user.update).mockResolvedValue(mockUser as any);

      const user = await usersService.updateUser('user-1', {
        name: 'Updated Name',
        firstName: 'Updated',
        lastName: 'Name',
      });

      expect(user).toEqual(mockUser);
      expect(prisma.user.update).toHaveBeenCalledWith({
        where: { id: 'user-1' },
        data: {
          name: 'Updated Name',
          firstName: 'Updated',
          lastName: 'Name',
        },
        select: expect.any(Object),
      });
    });
  });
});
