120 lines
4.6 KiB
TypeScript
120 lines
4.6 KiB
TypeScript
import { Result } from '@core/shared/domain/Result';
|
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
|
import type { Driver } from '../../domain/entities/Driver';
|
|
import type { League } from '../../domain/entities/League';
|
|
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
|
|
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
|
|
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
|
import type { StandingRepository } from '../../domain/repositories/StandingRepository';
|
|
|
|
export interface GetLeagueOwnerSummaryInput {
|
|
leagueId: string;
|
|
}
|
|
|
|
export type GetLeagueOwnerSummaryErrorCode = 'LEAGUE_NOT_FOUND' | 'OWNER_NOT_FOUND' | 'REPOSITORY_ERROR';
|
|
|
|
export interface GetLeagueOwnerSummaryResult {
|
|
league: League;
|
|
owner: Driver;
|
|
totalMembers: number;
|
|
activeMembers: number;
|
|
rating: number | null;
|
|
rank: number | null;
|
|
}
|
|
|
|
export class GetLeagueOwnerSummaryUseCase {
|
|
constructor(
|
|
private readonly leagueRepository: LeagueRepository,
|
|
private readonly driverRepository: DriverRepository,
|
|
private readonly leagueMembershipRepository: LeagueMembershipRepository,
|
|
private readonly standingRepository: StandingRepository,
|
|
) {}
|
|
|
|
async execute(
|
|
input: GetLeagueOwnerSummaryInput,
|
|
): Promise<Result<GetLeagueOwnerSummaryResult, ApplicationErrorCode<GetLeagueOwnerSummaryErrorCode, { message: string }>>> {
|
|
try {
|
|
const league = await this.leagueRepository.findById(input.leagueId);
|
|
|
|
if (!league) {
|
|
return Result.err({
|
|
code: 'LEAGUE_NOT_FOUND',
|
|
details: { message: 'League not found' },
|
|
});
|
|
}
|
|
|
|
const owner = await this.driverRepository.findById(league.ownerId.toString());
|
|
|
|
if (!owner) {
|
|
return Result.err({
|
|
code: 'OWNER_NOT_FOUND',
|
|
details: { message: 'League owner not found' },
|
|
});
|
|
}
|
|
|
|
const members = await this.leagueMembershipRepository.getLeagueMembers(input.leagueId);
|
|
const totalMembers = members.length;
|
|
const activeMembers = members.filter(m => m.status.toString() === 'active').length;
|
|
|
|
// Calculate rating and rank for the owner
|
|
let rating: number | null = null;
|
|
let rank: number | null = null;
|
|
|
|
try {
|
|
// Get standing for the owner in this league
|
|
const ownerStanding = await this.standingRepository.findByDriverIdAndLeagueId(owner.id.toString(), league.id.toString());
|
|
|
|
if (ownerStanding) {
|
|
// Calculate rating from standing
|
|
const baseRating = 1000;
|
|
const pointsBonus = ownerStanding.points.toNumber() * 2;
|
|
const positionBonus = Math.max(0, 50 - (ownerStanding.position.toNumber() * 2));
|
|
const winBonus = ownerStanding.wins * 100;
|
|
|
|
rating = Math.round(baseRating + pointsBonus + positionBonus + winBonus);
|
|
|
|
// Calculate rank among all drivers in this league
|
|
const leagueStandings = await this.standingRepository.findByLeagueId(league.id.toString());
|
|
const driverStats = new Map<string, { rating: number }>();
|
|
|
|
for (const standing of leagueStandings) {
|
|
const driverId = standing.driverId.toString();
|
|
const standingBaseRating = 1000;
|
|
const standingPointsBonus = standing.points.toNumber() * 2;
|
|
const standingPositionBonus = Math.max(0, 50 - (standing.position.toNumber() * 2));
|
|
const standingWinBonus = standing.wins * 100;
|
|
|
|
const standingRating = Math.round(standingBaseRating + standingPointsBonus + standingPositionBonus + standingWinBonus);
|
|
driverStats.set(driverId, { rating: standingRating });
|
|
}
|
|
|
|
const rankings = Array.from(driverStats.entries())
|
|
.sort(([, a], [, b]) => b.rating - a.rating)
|
|
.map(([driverId], index) => ({ driverId, rank: index + 1 }));
|
|
|
|
const ownerRanking = rankings.find(r => r.driverId === owner.id.toString());
|
|
rank = ownerRanking ? ownerRanking.rank : null;
|
|
}
|
|
} catch (error) {
|
|
// If rating calculation fails, continue with null values
|
|
console.error('Failed to calculate rating/rank:', error);
|
|
}
|
|
|
|
return Result.ok({
|
|
league,
|
|
owner,
|
|
totalMembers,
|
|
activeMembers,
|
|
rating,
|
|
rank,
|
|
});
|
|
} catch (error: unknown) {
|
|
const message = error instanceof Error ? error.message : 'Failed to get league owner summary';
|
|
|
|
return Result.err({
|
|
code: 'REPOSITORY_ERROR',
|
|
details: { message },
|
|
});
|
|
}
|
|
}
|
|
} |