import { LeaguesApiClient } from "@/lib/api/leagues/LeaguesApiClient"; import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient"; import type { LeagueConfigFormModel } from "@/lib/types/LeagueConfigFormModel"; import type { LeagueScoringPresetDTO } from "@/lib/types/generated/LeagueScoringPresetDTO"; import { LeagueSettingsViewModel } from "@/lib/view-models/LeagueSettingsViewModel"; import { DriverSummaryViewModel } from "@/lib/view-models/DriverSummaryViewModel"; import type { LeagueScoringPresetViewModel } from "@/lib/view-models/LeagueScoringPresetViewModel"; import type { CustomPointsConfig } from "@/lib/view-models/ScoringConfigurationViewModel"; /** * League Settings Service * * Orchestrates league settings operations by coordinating API calls and view model creation. * All dependencies are injected via constructor. */ export class LeagueSettingsService { constructor( private readonly leaguesApiClient: LeaguesApiClient, private readonly driversApiClient: DriversApiClient ) {} /** * Get league settings with view model transformation */ async getLeagueSettings(leagueId: string): Promise { try { // Get league basic info (includes ownerId in DTO) const allLeagues = await this.leaguesApiClient.getAllWithCapacity(); const leagueDto = allLeagues.leagues.find(l => l.id === leagueId); if (!leagueDto) return null; const league = { id: leagueDto.id, name: leagueDto.name, ownerId: leagueDto.ownerId, createdAt: leagueDto.createdAt || new Date().toISOString(), }; // Get config const configDto = await this.leaguesApiClient.getLeagueConfig(leagueId); const config: LeagueConfigFormModel = (configDto.form ?? undefined) as unknown as LeagueConfigFormModel; // Get presets const presetsDto = await this.leaguesApiClient.getScoringPresets(); const presets: LeagueScoringPresetDTO[] = presetsDto.presets; // Get leaderboard once so we can hydrate rating / rank for owner + members const leaderboardDto = await this.driversApiClient.getLeaderboard(); const leaderboardByDriverId = new Map( leaderboardDto.drivers.map(driver => [driver.id, driver]) ); // Get owner const ownerDriver = await this.driversApiClient.getDriver(league.ownerId); let owner: DriverSummaryViewModel | null = null; if (ownerDriver) { const ownerStats = leaderboardByDriverId.get(ownerDriver.id); owner = new DriverSummaryViewModel({ driver: ownerDriver, rating: ownerStats?.rating ?? null, rank: ownerStats?.rank ?? null, }); } // Get members const membershipsDto = await this.leaguesApiClient.getMemberships(leagueId); const members: DriverSummaryViewModel[] = []; for (const member of membershipsDto.members) { if (member.driverId !== league.ownerId && member.role !== 'owner') { const driver = await this.driversApiClient.getDriver(member.driverId); if (driver) { const memberStats = leaderboardByDriverId.get(driver.id); members.push(new DriverSummaryViewModel({ driver, rating: memberStats?.rating ?? null, rank: memberStats?.rank ?? null, })); } } } return new LeagueSettingsViewModel({ league, config, presets, owner, members, }); } catch (error) { console.error('Failed to load league settings:', error); return null; } } /** * Transfer league ownership */ async transferOwnership(leagueId: string, currentOwnerId: string, newOwnerId: string): Promise { try { const result = await this.leaguesApiClient.transferOwnership(leagueId, currentOwnerId, newOwnerId); return result.success; } catch (error) { console.error('Failed to transfer ownership:', error); throw error; } } /** * Select a scoring preset */ selectScoringPreset( currentForm: LeagueConfigFormModel, presetId: string ): LeagueConfigFormModel { return { ...currentForm, scoring: { ...currentForm.scoring, patternId: presetId, customScoringEnabled: false, }, }; } /** * Toggle custom scoring */ toggleCustomScoring(currentForm: LeagueConfigFormModel): LeagueConfigFormModel { return { ...currentForm, scoring: { ...currentForm.scoring, customScoringEnabled: !currentForm.scoring.customScoringEnabled, }, }; } /** * Update championship settings */ updateChampionship( currentForm: LeagueConfigFormModel, key: keyof LeagueConfigFormModel['championships'], value: boolean ): LeagueConfigFormModel { return { ...currentForm, championships: { ...currentForm.championships, [key]: value, }, }; } /** * Get preset emoji based on name */ getPresetEmoji(preset: LeagueScoringPresetViewModel): string { const name = preset.name.toLowerCase(); if (name.includes('sprint') || name.includes('double')) return '⚡'; if (name.includes('endurance') || name.includes('long')) return '🏆'; if (name.includes('club') || name.includes('casual')) return '🏅'; return '🏁'; } /** * Get preset description based on name */ getPresetDescription(preset: LeagueScoringPresetViewModel): string { const name = preset.name.toLowerCase(); if (name.includes('sprint')) return 'Sprint + Feature race'; if (name.includes('endurance')) return 'Long-form endurance'; if (name.includes('club')) return 'Casual league format'; return preset.sessionSummary; } /** * Get preset info content for flyout */ getPresetInfoContent(presetName: string): { title: string; description: string; details: string[] } { const name = presetName.toLowerCase(); if (name.includes('sprint')) { return { title: 'Sprint + Feature Format', description: 'A two-race weekend format with a shorter sprint race and a longer feature race.', details: [ 'Sprint race typically awards reduced points (e.g., 8-6-4-3-2-1)', 'Feature race awards full points (e.g., 25-18-15-12-10-8-6-4-2-1)', 'Grid for feature often based on sprint results', 'Great for competitive leagues with time for multiple races', ], }; } if (name.includes('endurance') || name.includes('long')) { return { title: 'Endurance Format', description: 'Long-form racing focused on consistency and strategy over raw pace.', details: [ 'Single race per weekend, longer duration (60-90+ minutes)', 'Higher points for finishing (rewards reliability)', 'Often includes mandatory pit stops', 'Best for serious leagues with dedicated racers', ], }; } if (name.includes('club') || name.includes('casual')) { return { title: 'Club/Casual Format', description: 'Relaxed format perfect for community leagues and casual racing.', details: [ 'Simple points structure, easy to understand', 'Typically single race per weekend', 'Lower stakes, focus on participation', 'Great for beginners or mixed-skill leagues', ], }; } return { title: 'Standard Race Format', description: 'Traditional single-race weekend with standard F1-style points.', details: [ 'Points: 25-18-15-12-10-8-6-4-2-1 for top 10', 'Bonus points for pole position and fastest lap', 'One race per weekend', 'The most common format used in sim racing', ], }; } /** * Get championship info content for flyout */ getChampionshipInfoContent(key: string): { title: string; description: string; details: string[] } { const info: Record = { enableDriverChampionship: { title: 'Driver Championship', description: 'Track individual driver performance across all races in the season.', details: [ 'Each driver accumulates points based on race finishes', 'The driver with most points at season end wins', 'Standard in all racing leagues', 'Shows overall driver skill and consistency', ], }, enableTeamChampionship: { title: 'Team Championship', description: 'Combine points from all drivers within a team for team standings.', details: [ 'All drivers\' points count toward team total', 'Rewards having consistent performers across the roster', 'Creates team strategy opportunities', 'Only available in Teams mode leagues', ], }, enableNationsChampionship: { title: 'Nations Cup', description: 'Group drivers by nationality for international competition.', details: [ 'Drivers represent their country automatically', 'Points pooled by nationality', 'Adds international rivalry element', 'Great for diverse, international leagues', ], }, enableTrophyChampionship: { title: 'Trophy Championship', description: 'A special category championship for specific classes or groups.', details: [ 'Custom category you define (e.g., Am drivers, rookies)', 'Separate standings from main championship', 'Encourages participation from all skill levels', 'Can be used for gentleman drivers, newcomers, etc.', ], }, }; return info[key] || { title: 'Championship', description: 'A championship standings category.', details: ['Enable to track this type of championship.'], }; } }