import { Test, TestingModule } from '@nestjs/testing';
import { DriverService } from './DriverService';
import { GetDriversLeaderboardUseCase } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase';
import { GetTotalDriversUseCase } from '@core/racing/application/use-cases/GetTotalDriversUseCase';
import { CompleteDriverOnboardingUseCase } from '@core/racing/application/use-cases/CompleteDriverOnboardingUseCase';
import { IsDriverRegisteredForRaceUseCase } from '@core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase';
import type { Logger } from '@core/shared/application';

describe('DriverService', () => {
  let service: DriverService;
  let getDriversLeaderboardUseCase: jest.Mocked<GetDriversLeaderboardUseCase>;
  let getTotalDriversUseCase: jest.Mocked<GetTotalDriversUseCase>;
  let completeDriverOnboardingUseCase: jest.Mocked<CompleteDriverOnboardingUseCase>;
  let isDriverRegisteredForRaceUseCase: jest.Mocked<IsDriverRegisteredForRaceUseCase>;
  let logger: jest.Mocked<Logger>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        DriverService,
        {
          provide: 'GetDriversLeaderboardUseCase',
          useValue: {
            execute: jest.fn(),
          },
        },
        {
          provide: 'GetTotalDriversUseCase',
          useValue: {
            execute: jest.fn(),
          },
        },
        {
          provide: 'CompleteDriverOnboardingUseCase',
          useValue: {
            execute: jest.fn(),
          },
        },
        {
          provide: 'IsDriverRegisteredForRaceUseCase',
          useValue: {
            execute: jest.fn(),
          },
        },
        {
          provide: 'Logger',
          useValue: {
            debug: jest.fn(),
          },
        },
      ],
    }).compile();

    service = module.get<DriverService>(DriverService);
    getDriversLeaderboardUseCase = module.get('GetDriversLeaderboardUseCase');
    getTotalDriversUseCase = module.get('GetTotalDriversUseCase');
    completeDriverOnboardingUseCase = module.get('CompleteDriverOnboardingUseCase');
    isDriverRegisteredForRaceUseCase = module.get('IsDriverRegisteredForRaceUseCase');
    logger = module.get('Logger');
  });

  describe('getDriversLeaderboard', () => {
    it('should call GetDriversLeaderboardUseCase and return the view model', async () => {
      const mockViewModel = {
        drivers: [
          {
            id: 'driver-1',
            name: 'Driver 1',
            rating: 2500,
            skillLevel: 'Pro',
            nationality: 'DE',
            racesCompleted: 50,
            wins: 10,
            podiums: 20,
            isActive: true,
            rank: 1,
            avatarUrl: 'https://example.com/avatar1.png',
          },
        ],
        totalRaces: 50,
        totalWins: 10,
        activeCount: 1,
      };

      const mockPresenter = {
        viewModel: mockViewModel,
      };

      getDriversLeaderboardUseCase.execute.mockImplementation(async (input, presenter) => {
        Object.assign(presenter, mockPresenter);
      });

      const result = await service.getDriversLeaderboard();

      expect(getDriversLeaderboardUseCase.execute).toHaveBeenCalledWith(undefined, expect.any(Object));
      expect(logger.debug).toHaveBeenCalledWith('[DriverService] Fetching drivers leaderboard.');
      expect(result).toEqual(mockViewModel);
    });
  });

  describe('getTotalDrivers', () => {
    it('should call GetTotalDriversUseCase and return the view model', async () => {
      const mockViewModel = { totalDrivers: 5 };

      const mockPresenter = {
        viewModel: mockViewModel,
      };

      getTotalDriversUseCase.execute.mockImplementation(async (input, presenter) => {
        Object.assign(presenter, mockPresenter);
      });

      const result = await service.getTotalDrivers();

      expect(getTotalDriversUseCase.execute).toHaveBeenCalledWith(undefined, expect.any(Object));
      expect(logger.debug).toHaveBeenCalledWith('[DriverService] Fetching total drivers count.');
      expect(result).toEqual(mockViewModel);
    });
  });

  describe('completeOnboarding', () => {
    it('should call CompleteDriverOnboardingUseCase and return the view model', async () => {
      const input = {
        firstName: 'John',
        lastName: 'Doe',
        displayName: 'John Doe',
        country: 'US',
        timezone: 'America/New_York',
        bio: 'Racing enthusiast',
      };

      const mockViewModel = {
        success: true,
        driverId: 'user-123',
      };

      const mockPresenter = {
        viewModel: mockViewModel,
      };

      completeDriverOnboardingUseCase.execute.mockImplementation(async (input, presenter) => {
        Object.assign(presenter, mockPresenter);
      });

      const result = await service.completeOnboarding('user-123', input);

      expect(completeDriverOnboardingUseCase.execute).toHaveBeenCalledWith(
        {
          userId: 'user-123',
          ...input,
        },
        expect.any(Object)
      );
      expect(logger.debug).toHaveBeenCalledWith('Completing onboarding for user:', 'user-123');
      expect(result).toEqual(mockViewModel);
    });
  });

  describe('getDriverRegistrationStatus', () => {
    it('should call IsDriverRegisteredForRaceUseCase and return the view model', async () => {
      const query = {
        driverId: 'driver-1',
        raceId: 'race-1',
      };

      const mockViewModel = {
        isRegistered: true,
        raceId: 'race-1',
        driverId: 'driver-1',
      };

      const mockPresenter = {
        viewModel: mockViewModel,
      };

      isDriverRegisteredForRaceUseCase.execute.mockImplementation(async (params, presenter) => {
        Object.assign(presenter, mockPresenter);
      });

      const result = await service.getDriverRegistrationStatus(query);

      expect(isDriverRegisteredForRaceUseCase.execute).toHaveBeenCalledWith(query, expect.any(Object));
      expect(logger.debug).toHaveBeenCalledWith('Checking driver registration status:', query);
      expect(result).toEqual(mockViewModel);
    });
  });
});