/** * Application Use Case: GetSponsorDashboardUseCase * * Returns sponsor dashboard metrics including sponsorships, impressions, and investment data. */ import type { ISponsorRepository } from '../../domain/repositories/ISponsorRepository'; import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository'; import type { ISeasonRepository } from '../../domain/repositories/ISeasonRepository'; import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository'; import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository'; import type { IRaceRepository } from '../../domain/repositories/IRaceRepository'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort'; import { Money } from '../../domain/value-objects/Money'; export interface GetSponsorDashboardInput { sponsorId: string; } export interface SponsoredLeagueMetrics { drivers: number; races: number; impressions: number; } export type SponsoredLeagueStatus = 'active' | 'upcoming' | 'completed'; export interface SponsoredLeagueSummary { leagueId: string; leagueName: string; tier: 'main' | 'secondary'; metrics: SponsoredLeagueMetrics; status: SponsoredLeagueStatus; } export interface SponsorDashboardMetrics { impressions: number; impressionsChange: number; uniqueViewers: number; viewersChange: number; races: number; drivers: number; exposure: number; exposureChange: number; } export interface SponsorInvestmentSummary { activeSponsorships: number; totalInvestment: Money; costPerThousandViews: number; } export interface GetSponsorDashboardResult { sponsorId: string; sponsorName: string; metrics: SponsorDashboardMetrics; sponsoredLeagues: SponsoredLeagueSummary[]; investment: SponsorInvestmentSummary; } export type GetSponsorDashboardErrorCode = 'SPONSOR_NOT_FOUND' | 'REPOSITORY_ERROR'; export class GetSponsorDashboardUseCase { constructor( private readonly sponsorRepository: ISponsorRepository, private readonly seasonSponsorshipRepository: ISeasonSponsorshipRepository, private readonly seasonRepository: ISeasonRepository, private readonly leagueRepository: ILeagueRepository, private readonly leagueMembershipRepository: ILeagueMembershipRepository, private readonly raceRepository: IRaceRepository, private readonly output: UseCaseOutputPort, ) {} async execute( params: GetSponsorDashboardInput, ): Promise>> { try { const { sponsorId } = params; const sponsor = await this.sponsorRepository.findById(sponsorId); if (!sponsor) { return Result.err({ code: 'SPONSOR_NOT_FOUND', details: { message: 'Sponsor not found', }, }); } // Get all sponsorships for this sponsor const sponsorships = await this.seasonSponsorshipRepository.findBySponsorId(sponsorId); // Aggregate data across all sponsorships let totalImpressions = 0; let totalDrivers = 0; let totalRaces = 0; let totalInvestmentMoney = Money.create(0, 'USD'); const sponsoredLeagues: SponsoredLeagueSummary[] = []; const seenLeagues = new Set(); for (const sponsorship of sponsorships) { // Get season to find league const season = await this.seasonRepository.findById(sponsorship.seasonId); if (!season) continue; // Only process each league once if (seenLeagues.has(season.leagueId)) continue; seenLeagues.add(season.leagueId); const league = await this.leagueRepository.findById(season.leagueId); if (!league) continue; // Get membership count for this league const memberships = await this.leagueMembershipRepository.getLeagueMembers(season.leagueId); const driverCount = memberships.length; totalDrivers += driverCount; // Get races for this league const races = await this.raceRepository.findByLeagueId(season.leagueId); const raceCount = races.length; totalRaces += raceCount; // Calculate impressions based on completed races and drivers const completedRaces = races.filter(r => r.status === 'completed').length; const leagueImpressions = completedRaces * driverCount * 100; // Simplified: 100 views per driver per race totalImpressions += leagueImpressions; // Determine status based on season dates const now = new Date(); let status: SponsoredLeagueStatus = 'active'; if (season.endDate && season.endDate < now) { status = 'completed'; } else if (season.startDate && season.startDate > now) { status = 'upcoming'; } // Add investment totalInvestmentMoney = totalInvestmentMoney.add( Money.create(sponsorship.pricing.amount, sponsorship.pricing.currency), ); sponsoredLeagues.push({ leagueId: league.id.toString(), leagueName: league.name.toString(), tier: sponsorship.tier, metrics: { drivers: driverCount, races: raceCount, impressions: leagueImpressions, }, status, }); } const activeSponsorships = sponsorships.filter(s => s.status === 'active').length; const costPerThousandViews = totalImpressions > 0 ? totalInvestmentMoney.amount / (totalImpressions / 1000) : 0; // Calculate unique viewers (simplified: assume 70% of impressions are unique) const uniqueViewers = Math.round(totalImpressions * 0.7); // Calculate exposure score (0-100 based on tier distribution) const mainSponsorships = sponsorships.filter(s => s.tier === 'main').length; const exposure = sponsorships.length > 0 ? Math.min(100, (mainSponsorships * 30) + (sponsorships.length * 10)) : 0; const result: GetSponsorDashboardResult = { sponsorId, sponsorName: sponsor.name, metrics: { impressions: totalImpressions, impressionsChange: 0, uniqueViewers, viewersChange: 0, races: totalRaces, drivers: totalDrivers, exposure, exposureChange: 0, }, sponsoredLeagues, investment: { activeSponsorships, totalInvestment: totalInvestmentMoney, costPerThousandViews: Math.round(costPerThousandViews * 100) / 100, }, }; this.output.present(result); return Result.ok(undefined); } catch (err) { const error = err as { message?: string } | undefined; return Result.err({ code: 'REPOSITORY_ERROR', details: { message: error?.message ?? 'Failed to fetch sponsor dashboard', }, }); } } }