import { ViewModel } from "../contracts/view-models/ViewModel"; import type { LeagueDetailPageViewData, LeagueMembershipWithRole, SponsorInfo } from '../view-data/LeagueDetailPageViewData'; import { DriverViewModel } from './DriverViewModel'; import { RaceViewModel } from './RaceViewModel'; export interface DriverSummary { driver: DriverViewModel; rating: number | null; rank: number | null; } export class LeagueDetailPageViewModel extends ViewModel { private readonly data: LeagueDetailPageViewData; readonly allRaces: RaceViewModel[]; readonly runningRaces: RaceViewModel[]; readonly ownerSummary: DriverSummary | null; readonly adminSummaries: DriverSummary[]; readonly stewardSummaries: DriverSummary[]; constructor(data: LeagueDetailPageViewData) { super(); this.data = data; this.allRaces = data.allRaces.map(r => r instanceof RaceViewModel ? r : new RaceViewModel(r)); this.runningRaces = this.allRaces.filter(r => r.status === 'running'); // Build driver summaries this.ownerSummary = this.buildDriverSummary(data.ownerId); this.adminSummaries = data.memberships .filter(m => m.role === 'admin') .slice(0, 3) .map(m => this.buildDriverSummary(m.driverId)) .filter((s): s is DriverSummary => s !== null); this.stewardSummaries = data.memberships .filter(m => m.role === 'steward') .slice(0, 3) .map(m => this.buildDriverSummary(m.driverId)) .filter((s): s is DriverSummary => s !== null); } private buildDriverSummary(driverId: string): DriverSummary | null { const driverData = this.data.drivers.find(d => d.id === driverId) || (this.data.owner?.id === driverId ? this.data.owner : null); if (!driverData) return null; const driver = new DriverViewModel(driverData); return { driver, rating: null, rank: null, }; } get id(): string { return this.data.id; } get name(): string { return this.data.name; } get description(): string { return this.data.description ?? ''; } get ownerId(): string { return this.data.ownerId; } get createdAt(): string { return this.data.createdAt; } get settings() { return this.data.settings; } get socialLinks() { return this.data.socialLinks; } get owner() { return this.data.owner; } get scoringConfig() { return this.data.scoringConfig; } get drivers() { return this.data.drivers; } get memberships() { return this.data.memberships; } get averageSOF(): number | null { return this.data.averageSOF; } get completedRacesCount(): number { return this.data.completedRacesCount; } get sponsors(): SponsorInfo[] { return this.data.sponsors; } get sponsorInsights() { const memberCount = this.memberships.length; const mainSponsorTaken = this.sponsors.some(s => s.tier === 'main'); const secondaryTaken = this.sponsors.filter(s => s.tier === 'secondary').length; return { avgViewsPerRace: 5400 + memberCount * 50, totalImpressions: 45000 + memberCount * 500, engagementRate: (3.5 + (memberCount / 50)).toFixed(1), estimatedReach: memberCount * 150, mainSponsorAvailable: !mainSponsorTaken, secondarySlotsAvailable: Math.max(0, 2 - secondaryTaken), mainSponsorPrice: 800 + Math.floor(memberCount * 10), secondaryPrice: 250 + Math.floor(memberCount * 3), tier: (this.averageSOF && this.averageSOF > 3000 ? 'premium' : this.averageSOF && this.averageSOF > 2000 ? 'standard' : 'starter') as 'premium' | 'standard' | 'starter', trustScore: Math.min(100, 60 + memberCount + this.completedRacesCount), discordMembers: memberCount * 3, monthlyActivity: Math.min(100, 40 + this.completedRacesCount * 2), }; } get isSponsorMode(): boolean { return false; } get currentUserMembership(): LeagueMembershipWithRole | null { return null; } get canEndRaces(): boolean { return this.currentUserMembership?.role === 'admin' || this.currentUserMembership?.role === 'owner'; } }