import { Result } from '@core/shared/domain/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { SocialGraphRepository } from '@core/social/domain/repositories/SocialGraphRepository'; import type { Driver } from '../../domain/entities/Driver'; import type { Team } from '../../domain/entities/Team'; import type { DriverRepository } from '../../domain/repositories/DriverRepository'; import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository'; import type { TeamRepository } from '../../domain/repositories/TeamRepository'; import type { TeamMembership } from '../../domain/types/TeamMembership'; import type { DriverExtendedProfileProvider } from '../ports/DriverExtendedProfileProvider'; import type { DriverStats, DriverStatsUseCase } from './DriverStatsUseCase'; import type { DriverRanking, RankingUseCase } from './RankingUseCase'; export type GetProfileOverviewInput = { driverId: string; }; export interface ProfileOverviewStats { totalRaces: number; wins: number; podiums: number; dnfs: number; avgFinish: number | null; bestFinish: number | null; worstFinish: number | null; finishRate: number | null; winRate: number | null; podiumRate: number | null; percentile: number | null; rating: number | null; consistency: number | null; overallRank: number | null; } export interface ProfileOverviewFinishDistribution { totalRaces: number; wins: number; podiums: number; topTen: number; dnfs: number; other: number; } export interface ProfileOverviewTeamMembership { team: Team; membership: TeamMembership; } export interface ProfileOverviewSocialSummary { friendsCount: number; friends: Driver[]; } export interface ProfileOverviewDriverInfo { driver: Driver; totalDrivers: number; globalRank: number | null; consistency: number | null; rating: number | null; } export type GetProfileOverviewResult = { driverInfo: ProfileOverviewDriverInfo; stats: ProfileOverviewStats | null; finishDistribution: ProfileOverviewFinishDistribution | null; teamMemberships: ProfileOverviewTeamMembership[]; socialSummary: ProfileOverviewSocialSummary; extendedProfile: ReturnType; }; export type GetProfileOverviewErrorCode = | 'DRIVER_NOT_FOUND' | 'REPOSITORY_ERROR'; export class GetProfileOverviewUseCase { constructor(private readonly driverRepository: DriverRepository, private readonly teamRepository: TeamRepository, private readonly teamMembershipRepository: TeamMembershipRepository, private readonly socialRepository: SocialGraphRepository, private readonly driverExtendedProfileProvider: DriverExtendedProfileProvider, private readonly driverStatsUseCase: DriverStatsUseCase, private readonly rankingUseCase: RankingUseCase) {} async execute( input: GetProfileOverviewInput, ): Promise< Result> > { try { const { driverId } = input; const driver = await this.driverRepository.findById(driverId); if (!driver) { return Result.err({ code: 'DRIVER_NOT_FOUND', details: { message: 'Driver not found' }, }); } const [driverStats, teams, friends] = await Promise.all([ this.driverStatsUseCase.getDriverStats(driverId), this.teamRepository.findAll(), this.socialRepository.getFriends(driverId), ]); const driverInfo = await this.buildDriverInfo(driver, driverStats); const stats = this.buildStats(driverStats); const finishDistribution = this.buildFinishDistribution(driverStats); const teamMemberships = await this.buildTeamMemberships(driver.id, teams); const socialSummary = this.buildSocialSummary(friends); const extendedProfile = this.driverExtendedProfileProvider.getExtendedProfile(driverId); const result: GetProfileOverviewResult = { driverInfo, stats, finishDistribution, teamMemberships, socialSummary, extendedProfile, }; return Result.ok(result); } catch (error) { return Result.err({ code: 'REPOSITORY_ERROR', details: { message: error instanceof Error ? error.message : 'Failed to load profile overview', }, }); } } private async buildDriverInfo( driver: Driver, stats: DriverStats | null, ): Promise { const rankings = await this.rankingUseCase.getAllDriverRankings(); const fallbackRank = this.computeFallbackRank(driver.id, rankings); const totalDrivers = rankings.length; return { driver, totalDrivers, globalRank: stats?.overallRank ?? fallbackRank, consistency: stats?.consistency ?? null, rating: stats?.rating ?? null, }; } private computeFallbackRank( driverId: string, rankings: DriverRanking[], ): number | null { const index = rankings.findIndex(entry => entry.driverId === driverId); if (index === -1) { return null; } return index + 1; } private buildStats( stats: DriverStats | null, ): ProfileOverviewStats | null { if (!stats) { return null; } const totalRaces = stats.totalRaces; const dnfs = stats.dnfs; const finishedRaces = Math.max(totalRaces - dnfs, 0); const finishRate = totalRaces > 0 ? (finishedRaces / totalRaces) * 100 : null; const winRate = totalRaces > 0 ? (stats.wins / totalRaces) * 100 : null; const podiumRate = totalRaces > 0 ? (stats.podiums / totalRaces) * 100 : null; return { totalRaces, wins: stats.wins, podiums: stats.podiums, dnfs, avgFinish: stats.avgFinish, bestFinish: stats.bestFinish, worstFinish: stats.worstFinish, finishRate, winRate, podiumRate, percentile: null, // Not available in new DriverStats rating: stats.rating, consistency: stats.consistency, overallRank: stats.overallRank, }; } private buildFinishDistribution( stats: DriverStats | null, ): ProfileOverviewFinishDistribution | null { if (!stats || stats.totalRaces <= 0) { return null; } const totalRaces = stats.totalRaces; const dnfs = stats.dnfs; const finishedRaces = Math.max(totalRaces - dnfs, 0); const estimatedTopTen = Math.min( finishedRaces, Math.round(totalRaces * 0.7), ); const topTen = Math.max(estimatedTopTen, stats.podiums); const other = Math.max(totalRaces - topTen, 0); return { totalRaces, wins: stats.wins, podiums: stats.podiums, topTen, dnfs, other, }; } private async buildTeamMemberships( driverId: string, teams: Team[], ): Promise { const memberships: ProfileOverviewTeamMembership[] = []; for (const team of teams) { const membership = await this.teamMembershipRepository.getMembership( team.id, driverId, ); if (!membership) continue; memberships.push({ team, membership, }); } memberships.sort( (a, b) => a.membership.joinedAt.getTime() - b.membership.joinedAt.getTime(), ); return memberships; } private buildSocialSummary(friends: Driver[]): ProfileOverviewSocialSummary { return { friendsCount: friends.length, friends, }; } }