import { describe, it, expect, vi, Mocked } from 'vitest';
import { LeagueStewardingService } from './LeagueStewardingService';
import { RaceService } from '../races/RaceService';
import { ProtestService } from '../protests/ProtestService';
import { PenaltyService } from '../penalties/PenaltyService';
import { DriverService } from '../drivers/DriverService';
import { LeagueMembershipService } from './LeagueMembershipService';
import { LeagueStewardingViewModel } from '../../view-models/LeagueStewardingViewModel';

describe('LeagueStewardingService', () => {
  let mockRaceService: Mocked<RaceService>;
  let mockProtestService: Mocked<ProtestService>;
  let mockPenaltyService: Mocked<PenaltyService>;
  let mockDriverService: Mocked<DriverService>;
  let mockLeagueMembershipService: Mocked<LeagueMembershipService>;
  let service: LeagueStewardingService;

  beforeEach(() => {
    mockRaceService = {
      findByLeagueId: vi.fn(),
    } as Mocked<RaceService>;

    mockProtestService = {
      findByRaceId: vi.fn(),
      getProtestById: vi.fn(),
    } as Mocked<ProtestService>;

    mockPenaltyService = {
      findByRaceId: vi.fn(),
      getPenaltyTypesReference: vi.fn(),
    } as Mocked<PenaltyService>;

    mockDriverService = {
      findByIds: vi.fn(),
    } as Mocked<DriverService>;

    mockLeagueMembershipService = {} as Mocked<LeagueMembershipService>;

    service = new LeagueStewardingService(
      mockRaceService,
      mockProtestService,
      mockPenaltyService,
      mockDriverService,
      mockLeagueMembershipService
    );
  });

  describe('getLeagueStewardingData', () => {
    it('should return stewarding data with all races and their protests/penalties', async () => {
      const leagueId = 'league-123';

      // Mock races
      mockRaceService.findByLeagueId.mockResolvedValue([
        { id: 'race-1', track: 'Monza', scheduledAt: '2023-10-01T10:00:00Z' },
        { id: 'race-2', track: 'Silverstone', scheduledAt: '2023-09-15T10:00:00Z' },
      ]);

      // Mock protests for race-1
      mockProtestService.findByRaceId.mockImplementation(async (raceId) => {
        if (raceId === 'race-1') {
          return [
            { id: 'protest-1', protestingDriverId: 'driver-1', accusedDriverId: 'driver-2', status: 'pending' },
            { id: 'protest-2', protestingDriverId: 'driver-3', accusedDriverId: 'driver-4', status: 'upheld' },
          ];
        }
        return [];
      });

      // Mock penalties for race-1
      mockPenaltyService.findByRaceId.mockImplementation(async (raceId) => {
        if (raceId === 'race-1') {
          return [
            { id: 'penalty-1', driverId: 'driver-2', type: 'time', value: 5, reason: 'Incident' },
          ];
        }
        return [];
      });

      // Mock driver service
      mockDriverService.findByIds.mockResolvedValue([
        { id: 'driver-1', name: 'Driver 1' },
        { id: 'driver-2', name: 'Driver 2' },
        { id: 'driver-3', name: 'Driver 3' },
        { id: 'driver-4', name: 'Driver 4' },
      ]);

      const result = await service.getLeagueStewardingData(leagueId);

      expect(result).toBeInstanceOf(LeagueStewardingViewModel);
      expect(result.racesWithData).toHaveLength(2);
      expect(result.totalPending).toBe(1);
      expect(result.totalResolved).toBe(1);
      expect(result.totalPenalties).toBe(1);
      expect(result.pendingRaces).toHaveLength(1);
      expect(result.historyRaces).toHaveLength(1);
    });

    it('should handle empty races array', async () => {
      const leagueId = 'league-123';

      mockRaceService.findByLeagueId.mockResolvedValue([]);
      mockProtestService.findByRaceId.mockResolvedValue([]);
      mockPenaltyService.findByRaceId.mockResolvedValue([]);
      mockDriverService.findByIds.mockResolvedValue([]);

      const result = await service.getLeagueStewardingData(leagueId);

      expect(result.racesWithData).toHaveLength(0);
      expect(result.totalPending).toBe(0);
      expect(result.totalResolved).toBe(0);
      expect(result.totalPenalties).toBe(0);
    });
  });

  describe('reviewProtest', () => {
    it('should call protestService.reviewProtest', async () => {
      const input = {
        protestId: 'protest-123',
        stewardId: 'steward-456',
        decision: 'upheld',
        decisionNotes: 'Test notes',
      };

      mockProtestService.reviewProtest = vi.fn().mockResolvedValue(undefined);

      await service.reviewProtest(input);

      expect(mockProtestService.reviewProtest).toHaveBeenCalledWith(input);
    });
  });

  describe('applyPenalty', () => {
    it('should call penaltyService.applyPenalty', async () => {
      const input = {
        driverId: 'driver-123',
        raceId: 'race-456',
        type: 'time',
        value: 10,
        reason: 'Test reason',
      };

      mockPenaltyService.applyPenalty = vi.fn().mockResolvedValue(undefined);

      await service.applyPenalty(input);

      expect(mockPenaltyService.applyPenalty).toHaveBeenCalledWith(input);
    });
  });

  describe('getProtestDetailViewModel', () => {
    it('should combine protest details + penalty types into a page-ready view model', async () => {
      const leagueId = 'league-123';
      const protestId = 'protest-1';

      mockProtestService.getProtestById.mockResolvedValue({
        protest: { id: protestId, raceId: 'race-1', protestingDriverId: 'd1', accusedDriverId: 'd2', status: 'pending', submittedAt: '2023-10-01T10:00:00Z', description: 'desc' } as any,
        race: { id: 'race-1' } as any,
        protestingDriver: { id: 'd1', name: 'Driver 1' } as any,
        accusedDriver: { id: 'd2', name: 'Driver 2' } as any,
      });

      mockPenaltyService.getPenaltyTypesReference.mockResolvedValue({
        penaltyTypes: [
          { type: 'time_penalty', requiresValue: true, valueKind: 'seconds' },
          { type: 'warning', requiresValue: false, valueKind: 'none' },
        ],
        defaultReasons: { upheld: 'Upheld reason', dismissed: 'Dismissed reason' },
      } as any);

      const result = await service.getProtestDetailViewModel(leagueId, protestId);

      expect(mockProtestService.getProtestById).toHaveBeenCalledWith(leagueId, protestId);
      expect(mockPenaltyService.getPenaltyTypesReference).toHaveBeenCalled();
      expect(result.protest.id).toBe(protestId);
      expect(result.penaltyTypes.length).toBe(2);
      expect(result.defaultReasons.upheld).toBe('Upheld reason');
      expect(result.initialPenaltyType).toBe('time_penalty');
    });
  });
});