import { describe, it, expect, beforeEach, vi } from 'vitest';
import { GetDriversLeaderboardUseCase } from './GetDriversLeaderboardUseCase';
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
import type { IRankingService } from '../../domain/services/IRankingService';
import type { IDriverStatsService } from '../../domain/services/IDriverStatsService';
import type { IImageServicePort } from '../ports/IImageServicePort';
import type { Logger } from '@core/shared/application';

describe('GetDriversLeaderboardUseCase', () => {
  const mockDriverFindAll = vi.fn();
  const mockDriverRepo: IDriverRepository = {
    findById: vi.fn(),
    findAll: mockDriverFindAll,
    create: vi.fn(),
    update: vi.fn(),
    delete: vi.fn(),
    exists: vi.fn(),
  };

  const mockRankingGetAllDriverRankings = vi.fn();
  const mockRankingService: IRankingService = {
    getAllDriverRankings: mockRankingGetAllDriverRankings,
  };

  const mockDriverStatsGetDriverStats = vi.fn();
  const mockDriverStatsService: IDriverStatsService = {
    getDriverStats: mockDriverStatsGetDriverStats,
  };

  const mockImageGetDriverAvatar = vi.fn();
  const mockImageService: IImageServicePort = {
    getDriverAvatar: mockImageGetDriverAvatar,
  };

  const mockLogger: Logger = {
    debug: vi.fn(),
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('should return drivers leaderboard data', async () => {
    const useCase = new GetDriversLeaderboardUseCase(
      mockDriverRepo,
      mockRankingService,
      mockDriverStatsService,
      mockImageService,
      mockLogger,
    );

    const driver1 = { id: 'driver1', name: 'Driver One' };
    const driver2 = { id: 'driver2', name: 'Driver Two' };
    const rankings = { driver1: 1, driver2: 2 };
    const stats1 = { wins: 5, losses: 2 };
    const stats2 = { wins: 3, losses: 1 };

    mockDriverFindAll.mockResolvedValue([driver1, driver2]);
    mockRankingGetAllDriverRankings.mockReturnValue(rankings);
    mockDriverStatsGetDriverStats.mockImplementation((id) => {
      if (id === 'driver1') return stats1;
      if (id === 'driver2') return stats2;
      return null;
    });
    mockImageGetDriverAvatar.mockImplementation((id) => `avatar-${id}`);

    const result = await useCase.execute();

    expect(result.isOk()).toBe(true);
    expect(result.value).toEqual({
      drivers: [driver1, driver2],
      rankings,
      stats: { driver1: stats1, driver2: stats2 },
      avatarUrls: { driver1: 'avatar-driver1', driver2: 'avatar-driver2' },
    });
  });

  it('should return empty result when no drivers', async () => {
    const useCase = new GetDriversLeaderboardUseCase(
      mockDriverRepo,
      mockRankingService,
      mockDriverStatsService,
      mockImageService,
      mockLogger,
    );

    mockDriverFindAll.mockResolvedValue([]);
    mockRankingGetAllDriverRankings.mockReturnValue({});

    const result = await useCase.execute();

    expect(result.isOk()).toBe(true);
    expect(result.value).toEqual({
      drivers: [],
      rankings: {},
      stats: {},
      avatarUrls: {},
    });
  });

  it('should handle drivers without stats', async () => {
    const useCase = new GetDriversLeaderboardUseCase(
      mockDriverRepo,
      mockRankingService,
      mockDriverStatsService,
      mockImageService,
      mockLogger,
    );

    const driver1 = { id: 'driver1', name: 'Driver One' };
    const rankings = { driver1: 1 };

    mockDriverFindAll.mockResolvedValue([driver1]);
    mockRankingGetAllDriverRankings.mockReturnValue(rankings);
    mockDriverStatsGetDriverStats.mockReturnValue(null);
    mockImageGetDriverAvatar.mockReturnValue('avatar-driver1');

    const result = await useCase.execute();

    expect(result.isOk()).toBe(true);
    expect(result.value).toEqual({
      drivers: [driver1],
      rankings,
      stats: {},
      avatarUrls: { driver1: 'avatar-driver1' },
    });
  });

  it('should return error when repository throws', async () => {
    const useCase = new GetDriversLeaderboardUseCase(
      mockDriverRepo,
      mockRankingService,
      mockDriverStatsService,
      mockImageService,
      mockLogger,
    );

    const error = new Error('Repository error');
    mockDriverFindAll.mockRejectedValue(error);

    const result = await useCase.execute();

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr().code).toBe('REPOSITORY_ERROR');
    expect(result.unwrapErr().details.message).toBe('Repository error');
  });
});