import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { RecalculateChampionshipStandingsUseCase } from './RecalculateChampionshipStandingsUseCase';
import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository';
import type { ILeagueScoringConfigRepository } from '../../domain/repositories/ILeagueScoringConfigRepository';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository';
import type { IChampionshipStandingRepository } from '../../domain/repositories/IChampionshipStandingRepository';
import type { Result } from '../../domain/entities/Result';
import type { Penalty } from '../../domain/entities/Penalty';
import { EventScoringService } from '../../domain/services/EventScoringService';
import { ChampionshipAggregator } from '../../domain/services/ChampionshipAggregator';

describe('RecalculateChampionshipStandingsUseCase', () => {
  let useCase: RecalculateChampionshipStandingsUseCase;
  let seasonRepository: { findById: Mock };
  let leagueScoringConfigRepository: { findBySeasonId: Mock };
  let raceRepository: { findByLeagueId: Mock };
  let resultRepository: { findByRaceId: Mock };
  let penaltyRepository: { findByRaceId: Mock };
  let championshipStandingRepository: { saveAll: Mock };
  let eventScoringService: { scoreSession: Mock };
  let championshipAggregator: { aggregate: Mock };

  beforeEach(() => {
    seasonRepository = { findById: vi.fn() };
    leagueScoringConfigRepository = { findBySeasonId: vi.fn() };
    raceRepository = { findByLeagueId: vi.fn() };
    resultRepository = { findByRaceId: vi.fn() };
    penaltyRepository = { findByRaceId: vi.fn() };
    championshipStandingRepository = { saveAll: vi.fn() };
    eventScoringService = { scoreSession: vi.fn() };
    championshipAggregator = { aggregate: vi.fn() };
    useCase = new RecalculateChampionshipStandingsUseCase(
      seasonRepository as unknown as ISeasonRepository,
      leagueScoringConfigRepository as unknown as ILeagueScoringConfigRepository,
      raceRepository as unknown as IRaceRepository,
      resultRepository as unknown as IResultRepository,
      penaltyRepository as unknown as IPenaltyRepository,
      championshipStandingRepository as unknown as IChampionshipStandingRepository,
      eventScoringService as unknown as EventScoringService,
      championshipAggregator as unknown as ChampionshipAggregator,
    );
  });

  it('should return season not found error', async () => {
    seasonRepository.findById.mockResolvedValue(null);

    const result = await useCase.execute({ seasonId: 'season-1', championshipId: 'champ-1' });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'SEASON_NOT_FOUND',
      details: { message: 'Season not found: season-1' },
    });
  });

  it('should return league scoring config not found error', async () => {
    seasonRepository.findById.mockResolvedValue({ id: 'season-1', leagueId: 'league-1' });
    leagueScoringConfigRepository.findBySeasonId.mockResolvedValue(null);

    const result = await useCase.execute({ seasonId: 'season-1', championshipId: 'champ-1' });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'LEAGUE_SCORING_CONFIG_NOT_FOUND',
      details: { message: 'League scoring config not found for season: season-1' },
    });
  });

  it('should return championship config not found error', async () => {
    seasonRepository.findById.mockResolvedValue({ id: 'season-1', leagueId: 'league-1' });
    leagueScoringConfigRepository.findBySeasonId.mockResolvedValue({ championships: [] });

    const result = await useCase.execute({ seasonId: 'season-1', championshipId: 'champ-1' });

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr()).toEqual({
      code: 'CHAMPIONSHIP_CONFIG_NOT_FOUND',
      details: { message: 'Championship config not found: champ-1' },
    });
  });

  it('should recalculate standings successfully', async () => {
    const season = { id: 'season-1', leagueId: 'league-1' };
    const championship = { id: 'champ-1', name: 'Champ 1', sessionTypes: ['main'], pointsTableBySessionType: {}, dropScorePolicy: {} };
    const leagueScoringConfig = { championships: [championship] };
    const races = [{ id: 'race-1', sessionType: 'race' }];
    const results: Result[] = [];
    const penalties: Penalty[] = [];
    const standings = [{ participant: 'driver-1', position: 1, totalPoints: 25, resultsCounted: 1, resultsDropped: 0 }];

    seasonRepository.findById.mockResolvedValue(season);
    leagueScoringConfigRepository.findBySeasonId.mockResolvedValue(leagueScoringConfig);
    raceRepository.findByLeagueId.mockResolvedValue(races);
    resultRepository.findByRaceId.mockResolvedValue(results);
    penaltyRepository.findByRaceId.mockResolvedValue(penalties);
    eventScoringService.scoreSession.mockReturnValue({});
    championshipAggregator.aggregate.mockReturnValue(standings);
    championshipStandingRepository.saveAll.mockResolvedValue(undefined);

    const result = await useCase.execute({ seasonId: 'season-1', championshipId: 'champ-1' });

    expect(result.isOk()).toBe(true);
    const dto = result.unwrap();
    expect(dto.seasonId).toBe('season-1');
    expect(dto.championshipId).toBe('champ-1');
    expect(dto.championshipName).toBe('Champ 1');
    expect(dto.rows).toEqual([{ participant: 'driver-1', position: 1, totalPoints: 25, resultsCounted: 1, resultsDropped: 0 }]);
  });
});