import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetLeagueDriverSeasonStatsUseCase } from './GetLeagueDriverSeasonStatsUseCase';
import type { IStandingRepository } from '../../domain/repositories/IStandingRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { DriverRatingPort } from '../ports/DriverRatingPort';

describe('GetLeagueDriverSeasonStatsUseCase', () => {
  const mockStandingFindByLeagueId = vi.fn();
  const mockResultFindByDriverIdAndLeagueId = vi.fn();
  const mockPenaltyFindByRaceId = vi.fn();
  const mockRaceFindByLeagueId = vi.fn();
  const mockDriverRatingGetRating = vi.fn();

  let useCase: GetLeagueDriverSeasonStatsUseCase;
  let standingRepository: IStandingRepository;
  let resultRepository: IResultRepository;
  let penaltyRepository: IPenaltyRepository;
  let raceRepository: IRaceRepository;
  let driverRatingPort: DriverRatingPort;

  beforeEach(() => {
    standingRepository = {
      findByLeagueId: mockStandingFindByLeagueId,
      findByDriverIdAndLeagueId: vi.fn(),
      findAll: vi.fn(),
      save: vi.fn(),
      saveMany: vi.fn(),
      delete: vi.fn(),
      deleteByLeagueId: vi.fn(),
      exists: vi.fn(),
      recalculate: vi.fn(),
    };
    resultRepository = {
      findByDriverIdAndLeagueId: mockResultFindByDriverIdAndLeagueId,
    };
    penaltyRepository = {
      findByRaceId: mockPenaltyFindByRaceId,
    };
    raceRepository = {
      findById: vi.fn(),
      findAll: vi.fn(),
      findByLeagueId: mockRaceFindByLeagueId,
      findUpcomingByLeagueId: vi.fn(),
      findCompletedByLeagueId: vi.fn(),
      findByStatus: vi.fn(),
      findByDateRange: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      delete: vi.fn(),
      exists: vi.fn(),
    };
    driverRatingPort = {
      getRating: mockDriverRatingGetRating,
    };

    useCase = new GetLeagueDriverSeasonStatsUseCase(
      standingRepository,
      resultRepository,
      penaltyRepository,
      raceRepository,
      driverRatingPort,
    );
  });

  it('should return league driver season stats for given league id', async () => {
    const params = { leagueId: 'league-1' };

    const mockStandings = [
      { driverId: 'driver-1', position: 1, points: 100, racesCompleted: 5 },
      { driverId: 'driver-2', position: 2, points: 80, racesCompleted: 5 },
    ];
    const mockRaces = [
      { id: 'race-1' },
      { id: 'race-2' },
    ];
    const mockPenalties = [
      { driverId: 'driver-1', status: 'applied', type: 'points_deduction', value: 10 },
    ];
    const mockResults = [{ position: 1 }];
    const mockRating = { rating: 1500, ratingChange: 50 };

    standingRepository.findByLeagueId.mockResolvedValue(mockStandings);
    raceRepository.findByLeagueId.mockResolvedValue(mockRaces);
    penaltyRepository.findByRaceId.mockImplementation((raceId) => {
      if (raceId === 'race-1') return Promise.resolve(mockPenalties);
      return Promise.resolve([]);
    });
    driverRatingPort.getRating.mockReturnValue(mockRating);
    resultRepository.findByDriverIdAndLeagueId.mockResolvedValue(mockResults);

    const result = await useCase.execute(params);

    expect(result.isOk()).toBe(true);
    const dto = result.value!;
    expect(dto.leagueId).toBe('league-1');
    expect(dto.standings).toEqual([
      { driverId: 'driver-1', position: 1, points: 100, racesCompleted: 5 },
      { driverId: 'driver-2', position: 2, points: 80, racesCompleted: 5 },
    ]);
    expect(dto.penalties.get('driver-1')).toEqual({ baseDelta: -10, bonusDelta: 0 });
    expect(dto.driverRatings.get('driver-1')).toEqual(mockRating);
    expect(dto.driverResults.get('driver-1')).toEqual(mockResults);
  });

  it('should handle no penalties', async () => {
    const params = { leagueId: 'league-1' };

    const mockStandings = [{ driverId: 'driver-1', position: 1, points: 100, racesCompleted: 5 }];
    const mockRaces = [{ id: 'race-1' }];
    const mockResults = [{ position: 1 }];
    const mockRating = { rating: null, ratingChange: null };

    standingRepository.findByLeagueId.mockResolvedValue(mockStandings);
    raceRepository.findByLeagueId.mockResolvedValue(mockRaces);
    penaltyRepository.findByRaceId.mockResolvedValue([]);
    driverRatingPort.getRating.mockReturnValue(mockRating);
    resultRepository.findByDriverIdAndLeagueId.mockResolvedValue(mockResults);

    const result = await useCase.execute(params);

    expect(result.isOk()).toBe(true);
    const dto = result.value!;
    expect(dto.penalties.size).toBe(0);
  });
});