import { describe, it, expect, vi, Mocked } from 'vitest';
import { DriverService } from './DriverService';
import { DriversApiClient } from '../../api/drivers/DriversApiClient';
import { DriverLeaderboardViewModel } from '../../view-models/DriverLeaderboardViewModel';
import { DriverViewModel } from '../../view-models/DriverViewModel';
import { CompleteOnboardingViewModel } from '../../view-models/CompleteOnboardingViewModel';

describe('DriverService', () => {
  let mockApiClient: Mocked<DriversApiClient>;
  let service: DriverService;

  beforeEach(() => {
    mockApiClient = {
      getLeaderboard: vi.fn(),
      completeOnboarding: vi.fn(),
      getCurrent: vi.fn(),
    } as Mocked<DriversApiClient>;

    service = new DriverService(mockApiClient);
  });

  describe('getDriverLeaderboard', () => {
    it('should call apiClient.getLeaderboard and return DriverLeaderboardViewModel', async () => {
      const mockDto = {
        drivers: [
          {
            id: 'driver-1',
            name: 'John Doe',
            rating: 2500,
            wins: 10,
            podiums: 15,
            totalRaces: 50,
            country: 'US',
            avatarUrl: 'https://example.com/avatar.jpg',
          },
        ],
      };

      mockApiClient.getLeaderboard.mockResolvedValue(mockDto);

      const result = await service.getDriverLeaderboard();

      expect(mockApiClient.getLeaderboard).toHaveBeenCalled();
      expect(result).toBeInstanceOf(DriverLeaderboardViewModel);
      expect(result.drivers).toHaveLength(1);
      expect(result.drivers[0].name).toBe('John Doe');
    });

    it('should throw error when apiClient.getLeaderboard fails', async () => {
      const error = new Error('API call failed');
      mockApiClient.getLeaderboard.mockRejectedValue(error);

      await expect(service.getDriverLeaderboard()).rejects.toThrow('API call failed');
    });
  });

  describe('completeDriverOnboarding', () => {
    it('should call apiClient.completeOnboarding and return CompleteOnboardingViewModel', async () => {
      const input = {
        iracingId: '123456',
        country: 'US',
      };

      const mockDto = {
        success: true,
        driverId: 'driver-123',
      };

      mockApiClient.completeOnboarding.mockResolvedValue(mockDto);

      const result = await service.completeDriverOnboarding(input);

      expect(mockApiClient.completeOnboarding).toHaveBeenCalledWith(input);
      expect(result).toBeInstanceOf(CompleteOnboardingViewModel);
      expect(result.success).toBe(true);
      expect(result.isSuccessful).toBe(true);
    });

    it('should throw error when apiClient.completeOnboarding fails', async () => {
      const input = {
        iracingId: '123456',
        country: 'US',
      };

      const error = new Error('API call failed');
      mockApiClient.completeOnboarding.mockRejectedValue(error);

      await expect(service.completeDriverOnboarding(input)).rejects.toThrow('API call failed');
    });
  });

  describe('getCurrentDriver', () => {
    it('should call apiClient.getCurrent and return DriverViewModel when driver exists', async () => {
      const mockDto = {
        id: 'driver-123',
        name: 'John Doe',
        avatarUrl: 'https://example.com/avatar.jpg',
        iracingId: '123456',
        rating: 2500,
      };

      mockApiClient.getCurrent.mockResolvedValue(mockDto);

      const result = await service.getCurrentDriver();

      expect(mockApiClient.getCurrent).toHaveBeenCalled();
      expect(result).toBeInstanceOf(DriverViewModel);
      expect(result?.id).toBe('driver-123');
      expect(result?.name).toBe('John Doe');
      expect(result?.hasIracingId).toBe(true);
      expect(result?.formattedRating).toBe('2500');
    });

    it('should return null when apiClient.getCurrent returns null', async () => {
      mockApiClient.getCurrent.mockResolvedValue(null);

      const result = await service.getCurrentDriver();

      expect(mockApiClient.getCurrent).toHaveBeenCalled();
      expect(result).toBeNull();
    });

    it('should throw error when apiClient.getCurrent fails', async () => {
      const error = new Error('API call failed');
      mockApiClient.getCurrent.mockRejectedValue(error);

      await expect(service.getCurrentDriver()).rejects.toThrow('API call failed');
    });
  });
});