import type { IDriverRepository } from '../../domain/repositories/IDriverRepository'; import type { ITeamRepository } from '../../domain/repositories/ITeamRepository'; import type { ITeamMembershipRepository } from '../../domain/repositories/ITeamMembershipRepository'; import type { IImageServicePort } from '../ports/IImageServicePort'; import type { ISocialGraphRepository } from '@core/social/domain/repositories/ISocialGraphRepository'; import type { DriverExtendedProfileProvider } from '../ports/DriverExtendedProfileProvider'; import type { Driver } from '../../domain/entities/Driver'; import type { Team } from '../../domain/entities/Team'; import type { TeamMembership } from '../../domain/types/TeamMembership'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { UseCaseOutputPort, UseCase } from '@core/shared/application'; interface ProfileDriverStatsAdapter { rating: number | null; wins: number; podiums: number; dnfs: number; totalRaces: number; avgFinish: number | null; bestFinish: number | null; worstFinish: number | null; overallRank: number | null; consistency: number | null; percentile: number | null; } interface DriverRankingEntry { driverId: string; rating: number; overallRank: number | null; } 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: unknown; }; export type GetProfileOverviewErrorCode = | 'DRIVER_NOT_FOUND' | 'REPOSITORY_ERROR'; export class GetProfileOverviewUseCase implements UseCase { constructor( private readonly driverRepository: IDriverRepository, private readonly teamRepository: ITeamRepository, private readonly teamMembershipRepository: ITeamMembershipRepository, private readonly socialRepository: ISocialGraphRepository, private readonly imageService: IImageServicePort, private readonly driverExtendedProfileProvider: DriverExtendedProfileProvider, private readonly getDriverStats: (driverId: string) => ProfileDriverStatsAdapter | null, private readonly getAllDriverRankings: () => DriverRankingEntry[], private readonly output: UseCaseOutputPort, ) {} 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 [statsAdapter, teams, friends] = await Promise.all([ Promise.resolve(this.getDriverStats(driverId)), this.teamRepository.findAll(), this.socialRepository.getFriends(driverId), ]); const driverInfo = this.buildDriverInfo(driver, statsAdapter); const stats = this.buildStats(statsAdapter); const finishDistribution = this.buildFinishDistribution(statsAdapter); const teamMemberships = await this.buildTeamMemberships(driver.id, teams as Team[]); const socialSummary = this.buildSocialSummary(friends as Driver[]); const extendedProfile = this.driverExtendedProfileProvider.getExtendedProfile(driverId); const result: GetProfileOverviewResult = { driverInfo, stats, finishDistribution, teamMemberships, socialSummary, extendedProfile, }; this.output.present(result); return Result.ok(undefined); } catch (error) { return Result.err({ code: 'REPOSITORY_ERROR', details: { message: error instanceof Error ? error.message : 'Failed to load profile overview', }, }); } } private buildDriverInfo( driver: Driver, stats: ProfileDriverStatsAdapter | null, ): ProfileOverviewDriverInfo { const rankings = this.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: DriverRankingEntry[], ): number | null { const index = rankings.findIndex(entry => entry.driverId === driverId); if (index === -1) { return null; } return index + 1; } private buildStats( stats: ProfileDriverStatsAdapter | 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: stats.percentile, rating: stats.rating, consistency: stats.consistency, overallRank: stats.overallRank, }; } private buildFinishDistribution( stats: ProfileDriverStatsAdapter | 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, }; } }