import type { Logger } from '@core/shared/application'; import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository'; import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository'; import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository'; import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository'; import type { IStandingRepository } from '@core/racing/domain/repositories/IStandingRepository'; import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository'; import type { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository'; import type { ITeamRepository } from '@core/racing/domain/repositories/ITeamRepository'; import type { ITeamMembershipRepository } from '@core/racing/domain/repositories/ITeamMembershipRepository'; import type { ISponsorRepository } from '@core/racing/domain/repositories/ISponsorRepository'; import type { ISeasonRepository } from '@core/racing/domain/repositories/ISeasonRepository'; import type { ILeagueScoringConfigRepository } from '@core/racing/domain/repositories/ILeagueScoringConfigRepository'; import type { ISeasonSponsorshipRepository } from '@core/racing/domain/repositories/ISeasonSponsorshipRepository'; import type { ISponsorshipRequestRepository } from '@core/racing/domain/repositories/ISponsorshipRequestRepository'; import type { ILeagueWalletRepository } from '@core/racing/domain/repositories/ILeagueWalletRepository'; import type { ITransactionRepository } from '@core/racing/domain/repositories/ITransactionRepository'; import type { Season } from '@core/racing/domain/entities/season/Season'; import { getLeagueScoringPresetById } from './LeagueScoringPresets'; import type { IProtestRepository } from '@core/racing/domain/repositories/IProtestRepository'; import type { IPenaltyRepository } from '@core/racing/domain/repositories/IPenaltyRepository'; import type { IFeedRepository } from '@core/social/domain/repositories/IFeedRepository'; import type { ISocialGraphRepository } from '@core/social/domain/repositories/ISocialGraphRepository'; import { createRacingSeed } from './racing/RacingSeed'; export type RacingSeedDependencies = { driverRepository: IDriverRepository; leagueRepository: ILeagueRepository; seasonRepository: ISeasonRepository; leagueScoringConfigRepository: ILeagueScoringConfigRepository; seasonSponsorshipRepository: ISeasonSponsorshipRepository; sponsorshipRequestRepository: ISponsorshipRequestRepository; leagueWalletRepository: ILeagueWalletRepository; transactionRepository: ITransactionRepository; protestRepository: IProtestRepository; penaltyRepository: IPenaltyRepository; raceRepository: IRaceRepository; resultRepository: IResultRepository; standingRepository: IStandingRepository; leagueMembershipRepository: ILeagueMembershipRepository; raceRegistrationRepository: IRaceRegistrationRepository; teamRepository: ITeamRepository; teamMembershipRepository: ITeamMembershipRepository; sponsorRepository: ISponsorRepository; feedRepository: IFeedRepository; socialGraphRepository: ISocialGraphRepository; }; export class SeedRacingData { constructor( private readonly logger: Logger, private readonly seedDeps: RacingSeedDependencies, ) {} async execute(): Promise { const existingDrivers = await this.seedDeps.driverRepository.findAll(); if (existingDrivers.length > 0) { this.logger.info('[Bootstrap] Racing seed skipped (drivers already exist), ensuring scoring configs'); await this.ensureScoringConfigsForExistingData(); return; } const seed = createRacingSeed(); let sponsorshipRequestsSeededViaRepo = false; const seedableSponsorshipRequests = this.seedDeps .sponsorshipRequestRepository as unknown as { seed?: (input: unknown) => void }; if (typeof seedableSponsorshipRequests.seed === 'function') { seedableSponsorshipRequests.seed(seed.sponsorshipRequests); sponsorshipRequestsSeededViaRepo = true; } for (const driver of seed.drivers) { try { await this.seedDeps.driverRepository.create(driver); } catch { // ignore duplicates } } for (const league of seed.leagues) { try { await this.seedDeps.leagueRepository.create(league); } catch { // ignore duplicates } } for (const season of seed.seasons) { try { await this.seedDeps.seasonRepository.create(season); } catch { // ignore duplicates } } const activeSeasons = seed.seasons.filter((season) => season.status === 'active'); for (const season of activeSeasons) { const presetId = this.selectScoringPresetIdForSeason(season); const preset = getLeagueScoringPresetById(presetId); if (!preset) { this.logger.warn( `[Bootstrap] Scoring preset not found (presetId=${presetId}, seasonId=${season.id}, leagueId=${season.leagueId})`, ); continue; } const scoringConfig = preset.createConfig({ seasonId: season.id }); try { await this.seedDeps.leagueScoringConfigRepository.save(scoringConfig); } catch { // ignore duplicates } } for (const sponsorship of seed.seasonSponsorships) { try { await this.seedDeps.seasonSponsorshipRepository.create(sponsorship); } catch { // ignore duplicates } } if (!sponsorshipRequestsSeededViaRepo) { for (const request of seed.sponsorshipRequests) { try { await this.seedDeps.sponsorshipRequestRepository.create(request); } catch { // ignore duplicates } } } for (const wallet of seed.leagueWallets) { try { await this.seedDeps.leagueWalletRepository.create(wallet); } catch { // ignore duplicates } } for (const tx of seed.leagueWalletTransactions) { try { await this.seedDeps.transactionRepository.create(tx); } catch { // ignore duplicates } } for (const protest of seed.protests) { try { await this.seedDeps.protestRepository.create(protest); } catch { // ignore duplicates } } for (const penalty of seed.penalties) { try { await this.seedDeps.penaltyRepository.create(penalty); } catch { // ignore duplicates } } for (const race of seed.races) { try { await this.seedDeps.raceRepository.create(race); } catch { // ignore duplicates } } try { await this.seedDeps.resultRepository.createMany(seed.results); } catch { // ignore duplicates } for (const membership of seed.leagueMemberships) { try { await this.seedDeps.leagueMembershipRepository.saveMembership(membership); } catch { // ignore duplicates } } for (const request of seed.leagueJoinRequests) { try { await this.seedDeps.leagueMembershipRepository.saveJoinRequest(request); } catch { // ignore duplicates } } for (const team of seed.teams) { try { await this.seedDeps.teamRepository.create(team); } catch { // ignore duplicates } } for (const sponsor of seed.sponsors) { try { await this.seedDeps.sponsorRepository.create(sponsor); } catch { // ignore duplicates } } for (const membership of seed.teamMemberships) { try { await this.seedDeps.teamMembershipRepository.saveMembership(membership); } catch { // ignore duplicates } } for (const request of seed.teamJoinRequests) { try { await this.seedDeps.teamMembershipRepository.saveJoinRequest(request); } catch { // ignore duplicates } } for (const registration of seed.raceRegistrations) { try { await this.seedDeps.raceRegistrationRepository.register(registration); } catch { // ignore duplicates } } try { await this.seedDeps.standingRepository.saveMany(seed.standings); } catch { // ignore duplicates } const seedableFeed = this.seedDeps.feedRepository as unknown as { seed?: (input: unknown) => void }; if (typeof seedableFeed.seed === 'function') { seedableFeed.seed({ drivers: seed.drivers, friendships: seed.friendships, feedEvents: seed.feedEvents, }); } const seedableSocial = this.seedDeps.socialGraphRepository as unknown as { seed?: (input: unknown) => void }; if (typeof seedableSocial.seed === 'function') { seedableSocial.seed({ drivers: seed.drivers, friendships: seed.friendships, feedEvents: seed.feedEvents, }); } this.logger.info( `[Bootstrap] Seeded racing data: drivers=${seed.drivers.length}, leagues=${seed.leagues.length}, races=${seed.races.length}`, ); } private async ensureScoringConfigsForExistingData(): Promise { const leagues = await this.seedDeps.leagueRepository.findAll(); for (const league of leagues) { const seasons = await this.seedDeps.seasonRepository.findByLeagueId(league.id.toString()); const activeSeasons = seasons.filter((season) => season.status === 'active'); for (const season of activeSeasons) { const existing = await this.seedDeps.leagueScoringConfigRepository.findBySeasonId(season.id); if (existing) continue; const presetId = this.selectScoringPresetIdForSeason(season); const preset = getLeagueScoringPresetById(presetId); if (!preset) { this.logger.warn( `[Bootstrap] Scoring preset not found (presetId=${presetId}, seasonId=${season.id}, leagueId=${season.leagueId})`, ); continue; } const scoringConfig = preset.createConfig({ seasonId: season.id }); try { await this.seedDeps.leagueScoringConfigRepository.save(scoringConfig); } catch { // ignore duplicates } } } } private selectScoringPresetIdForSeason(season: Season): string { if (season.leagueId === 'league-5' && season.status === 'active') { return 'sprint-main-driver'; } if (season.leagueId === 'league-3') { return season.id.endsWith('-b') ? 'sprint-main-team' : 'club-default-nations'; } const match = /^league-(\d+)$/.exec(season.leagueId); const leagueNumber = match ? Number(match[1]) : undefined; if (leagueNumber !== undefined) { switch (leagueNumber % 4) { case 0: return 'sprint-main-team'; case 1: return 'endurance-main-trophy'; case 2: return 'sprint-main-driver'; case 3: return 'club-default-nations'; } } return 'club-default'; } }