import type { ITeamRepository } from '@gridpilot/racing/domain/repositories/ITeamRepository'; import type { ITeamMembershipRepository } from '@gridpilot/racing/domain/repositories/ITeamMembershipRepository'; import type { IDriverRepository } from '@gridpilot/racing/domain/repositories/IDriverRepository'; import type { ITeamsLeaderboardPresenter } from '@gridpilot/racing/application/presenters/ITeamsLeaderboardPresenter'; import { SkillLevelService } from '@gridpilot/racing/domain/services/SkillLevelService'; interface DriverStatsAdapter { rating: number | null; wins: number; totalRaces: number; } /** * Use case: GetTeamsLeaderboardUseCase * * Plain constructor-injected dependencies (no decorators) to keep the * application layer framework-agnostic and compatible with test tooling. */ export class GetTeamsLeaderboardUseCase { constructor( private readonly teamRepository: ITeamRepository, private readonly teamMembershipRepository: ITeamMembershipRepository, private readonly driverRepository: IDriverRepository, private readonly getDriverStats: (driverId: string) => DriverStatsAdapter | null, public readonly presenter: ITeamsLeaderboardPresenter, ) {} async execute(): Promise { const allTeams = await this.teamRepository.findAll(); const teams: any[] = []; await Promise.all( allTeams.map(async (team) => { const memberships = await this.teamMembershipRepository.findByTeamId(team.id); const memberCount = memberships.length; let ratingSum = 0; let ratingCount = 0; let totalWins = 0; let totalRaces = 0; for (const membership of memberships) { const stats = this.getDriverStats(membership.driverId); if (!stats) continue; if (typeof stats.rating === 'number') { ratingSum += stats.rating; ratingCount += 1; } totalWins += stats.wins ?? 0; totalRaces += stats.totalRaces ?? 0; } const averageRating = ratingCount > 0 ? ratingSum / ratingCount : null; const performanceLevel = SkillLevelService.getTeamPerformanceLevel(averageRating); teams.push({ id: team.id, name: team.name, memberCount, rating: averageRating, totalWins, totalRaces, performanceLevel, isRecruiting: true, createdAt: new Date(), description: team.description, specialization: team.specialization as 'endurance' | 'sprint' | 'mixed' | undefined, region: team.region, languages: team.languages, }); }) ); const recruitingCount = teams.filter((t) => t.isRecruiting).length; this.presenter.present(teams, recruitingCount); } }