Files
gridpilot.gg/core/racing/application/use-cases/TeamRankingUseCase.ts
2025-12-30 12:25:45 +01:00

139 lines
4.7 KiB
TypeScript

/**
* Application Use Case: TeamRankingUseCase
*
* Computes team rankings from rating snapshots (ledger-based).
* Orchestrates repositories to provide team ranking data to presentation layer.
* Evolved from direct standings to use team rating events and snapshots.
*/
import type { Logger } from '@core/shared/application';
import type { ITeamRatingRepository } from '../../domain/repositories/ITeamRatingRepository';
import type { ITeamRepository } from '../../domain/repositories/ITeamRepository';
import type { ITeamRankingUseCase, TeamRanking } from './ITeamRankingUseCase';
export class TeamRankingUseCase implements ITeamRankingUseCase {
constructor(
private readonly teamRatingRepository: ITeamRatingRepository,
private readonly teamRepository: ITeamRepository,
private readonly logger: Logger
) {
this.logger.info('[TeamRankingUseCase] Initialized with ledger-based team rating repositories');
}
async getAllTeamRankings(): Promise<TeamRanking[]> {
this.logger.debug('[TeamRankingUseCase] Computing rankings from team rating snapshots');
try {
// Get all teams for name resolution
const teams = await this.teamRepository.findAll();
const teamMap = new Map(teams.map(t => [t.id, t]));
// Get all team IDs
const teamIds = Array.from(teamMap.keys());
if (teamIds.length === 0) {
this.logger.warn('[TeamRankingUseCase] No teams found');
return [];
}
// Get rating snapshots for all teams
const rankingPromises = teamIds.map(async (teamId) => {
const snapshot = await this.teamRatingRepository.findByTeamId(teamId);
const team = teamMap.get(teamId);
if (!snapshot || !team) {
return null;
}
return {
teamId,
teamName: team.name.toString(),
drivingRating: snapshot.driving.value,
adminTrustRating: snapshot.adminTrust.value,
overallRating: snapshot.overall,
eventCount: snapshot.eventCount,
lastUpdated: snapshot.lastUpdated,
overallRank: null, // Will be assigned after sorting
} as TeamRanking;
});
const rankings = (await Promise.all(rankingPromises)).filter(
(r): r is TeamRanking => r !== null
);
if (rankings.length === 0) {
this.logger.warn('[TeamRankingUseCase] No team rating snapshots found');
return [];
}
// Sort by overall rating descending and assign ranks
rankings.sort((a, b) => b.overallRating - a.overallRating);
rankings.forEach((r, idx) => r.overallRank = idx + 1);
this.logger.info(`[TeamRankingUseCase] Computed rankings for ${rankings.length} teams`);
return rankings;
} catch (error) {
this.logger.error('[TeamRankingUseCase] Error computing rankings:', error instanceof Error ? error : new Error(String(error)));
throw error;
}
}
async getTeamRanking(teamId: string): Promise<TeamRanking | null> {
this.logger.debug(`[TeamRankingUseCase] Getting ranking for team ${teamId}`);
try {
const snapshot = await this.teamRatingRepository.findByTeamId(teamId);
if (!snapshot) {
this.logger.warn(`[TeamRankingUseCase] No rating snapshot found for team ${teamId}`);
return null;
}
const team = await this.teamRepository.findById(teamId);
if (!team) {
this.logger.warn(`[TeamRankingUseCase] Team ${teamId} not found`);
return null;
}
// Get all teams to calculate rank
const allTeams = await this.teamRepository.findAll();
const allRankings: TeamRanking[] = [];
for (const t of allTeams) {
const s = await this.teamRatingRepository.findByTeamId(t.id);
if (s) {
allRankings.push({
teamId: t.id,
teamName: t.name.toString(),
drivingRating: s.driving.value,
adminTrustRating: s.adminTrust.value,
overallRating: s.overall,
eventCount: s.eventCount,
lastUpdated: s.lastUpdated,
overallRank: null,
});
}
}
// Sort and assign rank
allRankings.sort((a, b) => b.overallRating - a.overallRating);
const rank = allRankings.findIndex(r => r.teamId === teamId) + 1;
return {
teamId,
teamName: team.name.toString(),
drivingRating: snapshot.driving.value,
adminTrustRating: snapshot.adminTrust.value,
overallRating: snapshot.overall,
eventCount: snapshot.eventCount,
lastUpdated: snapshot.lastUpdated,
overallRank: rank,
};
} catch (error) {
this.logger.error(`[TeamRankingUseCase] Error getting team ranking:`, error instanceof Error ? error : new Error(String(error)));
throw error;
}
}
}