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, TeamsLeaderboardResultDTO, TeamsLeaderboardViewModel, } from '@gridpilot/racing/application/presenters/ITeamsLeaderboardPresenter'; import { SkillLevelService } from '@gridpilot/racing/domain/services/SkillLevelService'; import type { UseCase } from '@gridpilot/shared/application/UseCase'; 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 implements UseCase { constructor( private readonly teamRepository: ITeamRepository, private readonly teamMembershipRepository: ITeamMembershipRepository, private readonly driverRepository: IDriverRepository, private readonly getDriverStats: (driverId: string) => DriverStatsAdapter | null, ) {} async execute(_input: void, presenter: ITeamsLeaderboardPresenter): Promise { const allTeams = await this.teamRepository.findAll(); const teams: any[] = []; await Promise.all( allTeams.map(async (team) => { const memberships = await this.teamMembershipRepository.getTeamMembers(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, }); }) ); const recruitingCount = teams.filter((t) => t.isRecruiting).length; const result: TeamsLeaderboardResultDTO = { teams, recruitingCount, }; presenter.reset(); presenter.present(result); } }