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

121 lines
3.8 KiB
TypeScript

/**
* Application Use Case: TeamRatingFactoryUseCase
*
* Factory use case for creating team rating events from race results.
* This replaces direct team rating calculations with event-based approach.
* Mirrors the user rating factory pattern.
*/
import type { Logger } from '@core/shared/application';
import type { ITeamRaceResultsProvider } from '../ports/ITeamRaceResultsProvider';
import { TeamDrivingRatingEventFactory } from '@core/racing/domain/services/TeamDrivingRatingEventFactory';
import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';
export interface TeamRatingFactoryInput {
raceId: string;
}
export interface TeamRatingFactoryOutput {
success: boolean;
raceId: string;
events: TeamRatingEvent[];
errors: string[];
}
export class TeamRatingFactoryUseCase {
constructor(
private readonly teamRaceResultsProvider: ITeamRaceResultsProvider,
private readonly logger: Logger
) {
this.logger.info('[TeamRatingFactoryUseCase] Initialized');
}
async execute(input: TeamRatingFactoryInput): Promise<TeamRatingFactoryOutput> {
this.logger.debug(`[TeamRatingFactoryUseCase] Creating rating events for race ${input.raceId}`);
try {
// Load team race results
const teamRaceResults = await this.teamRaceResultsProvider.getTeamRaceResults(input.raceId);
if (!teamRaceResults) {
return {
success: false,
raceId: input.raceId,
events: [],
errors: ['Team race results not found'],
};
}
// Use factory to create events
const eventsByTeam = TeamDrivingRatingEventFactory.createDrivingEventsFromRace(teamRaceResults);
// Flatten events from all teams
const allEvents: TeamRatingEvent[] = [];
for (const [, events] of eventsByTeam) {
allEvents.push(...events);
}
if (allEvents.length === 0) {
this.logger.info(`[TeamRatingFactoryUseCase] No events generated for race ${input.raceId}`);
return {
success: true,
raceId: input.raceId,
events: [],
errors: [],
};
}
this.logger.info(`[TeamRatingFactoryUseCase] Generated ${allEvents.length} events for race ${input.raceId}`);
return {
success: true,
raceId: input.raceId,
events: allEvents,
errors: [],
};
} catch (error) {
const errorMsg = `Failed to create rating events: ${error instanceof Error ? error.message : 'Unknown error'}`;
this.logger.error('[TeamRatingFactoryUseCase] Error:', error instanceof Error ? error : new Error(String(error)));
return {
success: false,
raceId: input.raceId,
events: [],
errors: [errorMsg],
};
}
}
/**
* Create team rating events manually (for testing or manual adjustments)
*/
createManualEvents(
teamId: string,
dimension: string,
delta: number,
reason: string,
sourceType: 'race' | 'penalty' | 'vote' | 'adminAction' | 'manualAdjustment',
sourceId?: string
): TeamRatingEvent[] {
const source = sourceId ? { type: sourceType, id: sourceId } : { type: sourceType };
const event = TeamRatingEvent.create({
id: TeamRatingEventId.generate(),
teamId,
dimension: TeamRatingDimensionKey.create(dimension),
delta: TeamRatingDelta.create(delta),
occurredAt: new Date(),
createdAt: new Date(),
source: source,
reason: { code: reason },
visibility: { public: true },
version: 1,
});
return [event];
}
}