import { describe, it, expect, vi, Mocked } from 'vitest';
import { RaceResultsService } from './RaceResultsService';
import { RacesApiClient } from '../../api/races/RacesApiClient';
import { RaceResultsDetailViewModel } from '../../view-models/RaceResultsDetailViewModel';
import { RaceWithSOFViewModel } from '../../view-models/RaceWithSOFViewModel';
import { ImportRaceResultsSummaryViewModel } from '../../view-models/ImportRaceResultsSummaryViewModel';
import type { RaceResultsDetailDTO, RaceWithSOFDTO } from '../../types/generated';

describe('RaceResultsService', () => {
  let mockApiClient: Mocked<RacesApiClient>;
  let service: RaceResultsService;

  beforeEach(() => {
    mockApiClient = {
      getResultsDetail: vi.fn(),
      getWithSOF: vi.fn(),
      importResults: vi.fn(),
    } as Mocked<RacesApiClient>;

    service = new RaceResultsService(mockApiClient);
  });

  describe('getResultsDetail', () => {
    it('should call apiClient.getResultsDetail and return RaceResultsDetailViewModel', async () => {
      const raceId = 'race-123';
      const currentUserId = 'user-456';

      const mockDto: RaceResultsDetailDTO = {
        raceId,
        track: 'Test Track',
      };

      mockApiClient.getResultsDetail.mockResolvedValue(mockDto);

      const result = await service.getResultsDetail(raceId, currentUserId);

      expect(mockApiClient.getResultsDetail).toHaveBeenCalledWith(raceId);
      expect(result).toBeInstanceOf(RaceResultsDetailViewModel);
      expect(result.raceId).toBe(raceId);
      expect(result.track).toBe('Test Track');
    });

    it('should handle undefined currentUserId', async () => {
      const raceId = 'race-123';

      const mockDto: RaceResultsDetailDTO = {
        raceId,
        track: 'Test Track',
      };

      mockApiClient.getResultsDetail.mockResolvedValue(mockDto);

      const result = await service.getResultsDetail(raceId);

      expect(result).toBeInstanceOf(RaceResultsDetailViewModel);
      expect(result.currentUserId).toBe('');
    });

    it('should throw error when apiClient.getResultsDetail fails', async () => {
      const raceId = 'race-123';
      const currentUserId = 'user-456';

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

      await expect(service.getResultsDetail(raceId, currentUserId)).rejects.toThrow('API call failed');
    });
  });

  describe('getWithSOF', () => {
    it('should call apiClient.getWithSOF and return RaceWithSOFViewModel', async () => {
      const raceId = 'race-123';

      const mockDto: RaceWithSOFDTO = {
        id: raceId,
        track: 'Test Track',
      };

      mockApiClient.getWithSOF.mockResolvedValue(mockDto);

      const result = await service.getWithSOF(raceId);

      expect(mockApiClient.getWithSOF).toHaveBeenCalledWith(raceId);
      expect(result).toBeInstanceOf(RaceWithSOFViewModel);
      expect(result.id).toBe(raceId);
      expect(result.track).toBe('Test Track');
    });

    it('should throw error when apiClient.getWithSOF fails', async () => {
      const raceId = 'race-123';

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

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

  describe('importResults', () => {
    it('should call apiClient.importResults and return ImportRaceResultsSummaryViewModel', async () => {
      const raceId = 'race-123';
      const input = { raceId, results: [{ position: 1 }] };

      const mockDto = {
        success: true,
        raceId,
        driversProcessed: 10,
        resultsRecorded: 10,
        errors: ['Error 1'],
      };

      mockApiClient.importResults.mockResolvedValue(mockDto as any);

      const result = await service.importResults(raceId, input);

      expect(mockApiClient.importResults).toHaveBeenCalledWith(raceId, input);
      expect(result).toBeInstanceOf(ImportRaceResultsSummaryViewModel);
      expect(result.raceId).toBe(raceId);
      expect(result.driversProcessed).toBe(10);
      expect(result.resultsRecorded).toBe(10);
      expect(result.errors).toEqual(['Error 1']);
    });

    it('should handle successful import with no errors', async () => {
      const raceId = 'race-123';
      const input = { raceId, results: [] };

      const mockDto = {
        success: true,
        raceId,
        driversProcessed: 5,
        resultsRecorded: 5,
        errors: [],
      };

      mockApiClient.importResults.mockResolvedValue(mockDto as any);

      const result = await service.importResults(raceId, input);

      expect(result.driversProcessed).toBe(5);
      expect(result.resultsRecorded).toBe(5);
      expect(result.errors).toEqual([]);
    });

    it('should throw error when apiClient.importResults fails', async () => {
      const raceId = 'race-123';
      const input = { raceId, results: [] };

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

      await expect(service.importResults(raceId, input)).rejects.toThrow('API call failed');
    });
  });
});