import { describe, it, expect, vi, Mocked } from 'vitest';
import { RaceStewardingService } from './RaceStewardingService';
import { RacesApiClient } from '@/lib/api/races/RacesApiClient';
import { ProtestsApiClient } from '@/lib/api/protests/ProtestsApiClient';
import { PenaltiesApiClient } from '@/lib/api/penalties/PenaltiesApiClient';
import { RaceStewardingViewModel } from '@/lib/view-models/RaceStewardingViewModel';

describe('RaceStewardingService', () => {
  let mockRacesApiClient: Mocked<RacesApiClient>;
  let mockProtestsApiClient: Mocked<ProtestsApiClient>;
  let mockPenaltiesApiClient: Mocked<PenaltiesApiClient>;
  let service: RaceStewardingService;

  beforeEach(() => {
    mockRacesApiClient = {
      getDetail: vi.fn(),
    } as Mocked<RacesApiClient>;

    mockProtestsApiClient = {
      getRaceProtests: vi.fn(),
    } as Mocked<ProtestsApiClient>;

    mockPenaltiesApiClient = {
      getRacePenalties: vi.fn(),
    } as Mocked<PenaltiesApiClient>;

    service = new RaceStewardingService(mockRacesApiClient, mockProtestsApiClient, mockPenaltiesApiClient);
  });

  describe('getRaceStewardingData', () => {
    it('should return race stewarding data with all protests and penalties', async () => {
      const raceId = 'race-123';
      const driverId = 'driver-456';

      const raceDetailDto = {
        race: {
          id: raceId,
          track: 'Monza',
          scheduledAt: '2023-10-01T10:00:00Z',
          status: 'completed',
        },
        league: {
          id: 'league-1',
          name: 'Test League',
        },
      };

      const protestsDto = {
        protests: [
          {
            id: 'protest-1',
            protestingDriverId: 'driver-1',
            accusedDriverId: 'driver-2',
            incident: { lap: 5, description: 'Contact at turn 1' },
            filedAt: '2023-10-01T10:00:00Z',
            status: 'pending',
          },
          {
            id: 'protest-2',
            protestingDriverId: 'driver-3',
            accusedDriverId: 'driver-4',
            incident: { lap: 10, description: 'Off-track' },
            filedAt: '2023-10-01T10:05:00Z',
            status: 'upheld',
          },
        ],
        driverMap: {
          'driver-1': { id: 'driver-1', name: 'Driver 1' },
          'driver-2': { id: 'driver-2', name: 'Driver 2' },
          'driver-3': { id: 'driver-3', name: 'Driver 3' },
          'driver-4': { id: 'driver-4', name: 'Driver 4' },
        },
      };

      const penaltiesDto = {
        penalties: [
          { id: 'penalty-1', driverId: 'driver-2', type: 'time', value: 5, reason: 'Incident' },
          { id: 'penalty-2', driverId: 'driver-4', type: 'grid', value: 3, reason: 'Qualifying incident' },
        ],
        driverMap: {
          'driver-2': { id: 'driver-2', name: 'Driver 2' },
          'driver-4': { id: 'driver-4', name: 'Driver 4' },
        },
      };

      mockRacesApiClient.getDetail.mockResolvedValue(raceDetailDto as any);
      mockProtestsApiClient.getRaceProtests.mockResolvedValue(protestsDto as any);
      mockPenaltiesApiClient.getRacePenalties.mockResolvedValue(penaltiesDto as any);

      const result = await service.getRaceStewardingData(raceId, driverId);

      expect(mockRacesApiClient.getDetail).toHaveBeenCalledWith(raceId, driverId);
      expect(mockProtestsApiClient.getRaceProtests).toHaveBeenCalledWith(raceId);
      expect(mockPenaltiesApiClient.getRacePenalties).toHaveBeenCalledWith(raceId);

      expect(result).toBeInstanceOf(RaceStewardingViewModel);
      expect(result.race).toEqual(raceDetailDto.race);
      expect(result.league).toEqual(raceDetailDto.league);
      expect(result.protests).toHaveLength(2);
      expect(result.penalties).toHaveLength(2);
      expect(result.pendingProtests).toHaveLength(1);
      expect(result.resolvedProtests).toHaveLength(1);
      expect(result.pendingCount).toBe(1);
      expect(result.resolvedCount).toBe(1);
      expect(result.penaltiesCount).toBe(2);
    });

    it('should handle empty protests and penalties', async () => {
      const raceId = 'race-123';
      const driverId = 'driver-456';

      const raceDetailDto = {
        race: { id: raceId, track: 'Monza', scheduledAt: '2023-10-01T10:00:00Z', status: 'completed' },
        league: { id: 'league-1', name: 'Test League' },
      };

      mockRacesApiClient.getDetail.mockResolvedValue(raceDetailDto as any);
      mockProtestsApiClient.getRaceProtests.mockResolvedValue({ protests: [], driverMap: {} } as any);
      mockPenaltiesApiClient.getRacePenalties.mockResolvedValue({ penalties: [], driverMap: {} } as any);

      const result = await service.getRaceStewardingData(raceId, driverId);

      expect(result.protests).toHaveLength(0);
      expect(result.penalties).toHaveLength(0);
      expect(result.pendingCount).toBe(0);
      expect(result.resolvedCount).toBe(0);
      expect(result.penaltiesCount).toBe(0);
    });

    it('should throw error when any API call fails', async () => {
      const raceId = 'race-123';
      const driverId = 'driver-456';

      const error = new Error('API call failed');
      mockRacesApiClient.getDetail.mockRejectedValue(error);

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