Files
gridpilot.gg/adapters/bootstrap/racing/RacingSeasonSponsorshipFactory.ts
2025-12-29 19:44:11 +01:00

259 lines
9.3 KiB
TypeScript

import { faker } from '@faker-js/faker';
import type { League } from '@core/racing/domain/entities/League';
import { SponsorshipRequest } from '@core/racing/domain/entities/SponsorshipRequest';
import { Season } from '@core/racing/domain/entities/season/Season';
import { SeasonSponsorship } from '@core/racing/domain/entities/season/SeasonSponsorship';
import type { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
import type { SeasonStatusValue } from '@core/racing/domain/value-objects/SeasonStatus';
import { Money } from '@core/racing/domain/value-objects/Money';
import { seedId } from './SeedIdHelper';
export class RacingSeasonSponsorshipFactory {
constructor(
private readonly baseDate: Date,
private readonly persistence: 'postgres' | 'inmemory' = 'inmemory',
) {}
createSeasons(leagues: League[]): Season[] {
const seasons: Season[] = [];
for (const league of leagues) {
const leagueId = league.id.toString();
if (leagueId === seedId('league-5', this.persistence)) {
seasons.push(
Season.create({
id: seedId('season-1', this.persistence),
leagueId,
gameId: 'iracing',
name: 'Season 1 (GT Sprint)',
year: 2025,
order: 1,
status: 'active',
startDate: this.daysFromBase(-30),
}),
Season.create({
id: seedId('season-2', this.persistence),
leagueId,
gameId: 'iracing',
name: 'Season 2 (Endurance Cup)',
year: 2024,
order: 0,
status: 'completed',
startDate: this.daysFromBase(-120),
endDate: this.daysFromBase(-60),
}),
Season.create({
id: seedId('season-3', this.persistence),
leagueId,
gameId: 'iracing',
name: 'Season 3 (Planned)',
year: 2025,
order: 2,
status: 'planned',
startDate: this.daysFromBase(14),
}),
);
continue;
}
if (leagueId === seedId('league-3', this.persistence)) {
seasons.push(
Season.create({
id: seedId('league-3-season-a', this.persistence),
leagueId,
gameId: 'iracing',
name: 'Split Season A',
year: 2025,
order: 1,
status: 'active',
startDate: this.daysFromBase(-10),
}),
Season.create({
id: seedId('league-3-season-b', this.persistence),
leagueId,
gameId: 'iracing',
name: 'Split Season B',
year: 2025,
order: 2,
status: 'active',
startDate: this.daysFromBase(-3),
}),
);
continue;
}
const baseYear = this.baseDate.getUTCFullYear();
const seasonCount = leagueId === seedId('league-2', this.persistence) ? 1 : faker.number.int({ min: 1, max: 3 });
for (let i = 0; i < seasonCount; i++) {
const id = seedId(`${leagueId}-season-${i + 1}`, this.persistence);
const isFirst = i === 0;
const status: SeasonStatusValue =
leagueId === seedId('league-1', this.persistence) && isFirst
? 'active'
: leagueId === seedId('league-2', this.persistence)
? 'planned'
: isFirst
? faker.helpers.arrayElement(['active', 'planned'] as const)
: faker.helpers.arrayElement(['completed', 'archived', 'cancelled'] as const);
const startOffset =
status === 'active'
? faker.number.int({ min: -60, max: -1 })
: status === 'planned'
? faker.number.int({ min: 7, max: 60 })
: faker.number.int({ min: -200, max: -90 });
const endOffset =
status === 'completed' || status === 'archived' || status === 'cancelled'
? faker.number.int({ min: -89, max: -7 })
: undefined;
seasons.push(
Season.create({
id,
leagueId,
gameId: 'iracing',
name: `${faker.word.adjective()} ${faker.word.noun()} Season`,
year: baseYear + faker.number.int({ min: -1, max: 1 }),
order: i + 1,
status,
startDate: this.daysFromBase(startOffset),
...(endOffset !== undefined ? { endDate: this.daysFromBase(endOffset) } : {}),
}),
);
}
}
return seasons;
}
createSeasonSponsorships(seasons: Season[], sponsors: Sponsor[]): SeasonSponsorship[] {
const sponsorships: SeasonSponsorship[] = [];
const sponsorIds = sponsors.map((s) => s.id.toString());
for (const season of seasons) {
const expectedSeasonId = seedId('season-1', this.persistence);
const sponsorshipCount =
season.id === expectedSeasonId
? 2
: season.status.isActive()
? faker.number.int({ min: 0, max: 2 })
: faker.number.int({ min: 0, max: 1 });
const usedSponsorIds = new Set<string>();
for (let i = 0; i < sponsorshipCount; i++) {
const tier: SeasonSponsorship['tier'] =
i === 0 ? 'main' : faker.helpers.arrayElement(['secondary', 'secondary', 'main'] as const);
const sponsorIdCandidate = faker.helpers.arrayElement(sponsorIds);
const sponsorId = usedSponsorIds.has(sponsorIdCandidate)
? faker.helpers.arrayElement(sponsorIds)
: sponsorIdCandidate;
usedSponsorIds.add(sponsorId);
const base = SeasonSponsorship.create({
id: seedId(`season-sponsorship-${season.id}-${i + 1}`, this.persistence),
seasonId: season.id,
leagueId: season.leagueId,
sponsorId,
tier,
pricing: Money.create(
tier === 'main' ? faker.number.int({ min: 900, max: 2500 }) : faker.number.int({ min: 250, max: 900 }),
'USD',
),
createdAt: faker.date.recent({ days: 120, refDate: this.baseDate }),
description: tier === 'main' ? 'Main sponsor slot' : 'Secondary sponsor slot',
...(season.status.isActive()
? {
status: faker.helpers.arrayElement(['active', 'pending'] as const),
activatedAt: faker.date.recent({ days: 30, refDate: this.baseDate }),
}
: season.status.isCompleted() || season.status.isArchived()
? {
status: faker.helpers.arrayElement(['ended', 'cancelled'] as const),
endedAt: faker.date.recent({ days: 200, refDate: this.baseDate }),
}
: {
status: faker.helpers.arrayElement(['pending', 'cancelled'] as const),
}),
});
sponsorships.push(base);
}
}
return sponsorships;
}
createSponsorshipRequests(seasons: Season[], sponsors: Sponsor[]): SponsorshipRequest[] {
const requests: SponsorshipRequest[] = [];
const sponsorIds = sponsors.map((s) => s.id.toString());
for (const season of seasons) {
const expectedSeasonId = seedId('season-1', this.persistence);
const isHighTrafficDemo = season.id === expectedSeasonId;
const maxRequests =
isHighTrafficDemo
? 8
: season.status.isActive()
? faker.number.int({ min: 0, max: 4 })
: faker.number.int({ min: 0, max: 1 });
for (let i = 0; i < maxRequests; i++) {
const tier: SponsorshipRequest['tier'] =
i === 0 ? 'main' : faker.helpers.arrayElement(['secondary', 'secondary', 'main'] as const);
const sponsorId =
isHighTrafficDemo && i === 0
? seedId('demo-sponsor-1', this.persistence)
: faker.helpers.arrayElement(sponsorIds);
const offeredAmount = Money.create(
tier === 'main' ? faker.number.int({ min: 1000, max: 3500 }) : faker.number.int({ min: 200, max: 1200 }),
'USD',
);
const includeMessage = i % 3 !== 0;
const message = includeMessage
? faker.helpers.arrayElement([
'We would love to sponsor this season — high-quality sim racing content fits our brand.',
'We can provide prize pool support + gear giveaways for podium finishers.',
'Interested in the main slot: branding on liveries + broadcast overlays.',
'We want a secondary slot to test engagement before a bigger commitment.',
])
: undefined;
// A mix of statuses for edge cases (pending is what the UI lists)
const status =
season.status.isActive()
? faker.helpers.arrayElement(['pending', 'pending', 'pending', 'rejected', 'withdrawn'] as const)
: faker.helpers.arrayElement(['pending', 'rejected'] as const);
requests.push(
SponsorshipRequest.create({
id: seedId(`sponsorship-request-${season.id}-${i + 1}`, this.persistence),
sponsorId,
entityType: 'season',
entityId: season.id,
tier,
offeredAmount,
...(message !== undefined ? { message } : {}),
status,
createdAt: faker.date.recent({ days: 60, refDate: this.baseDate }),
}),
);
}
}
return requests;
}
private daysFromBase(days: number): Date {
return new Date(this.baseDate.getTime() + days * 24 * 60 * 60 * 1000);
}
}