/** * Use Case: ApplyForSponsorshipUseCase * * Allows a sponsor to apply for a sponsorship slot on any entity * (driver, team, race, or season/league). */ import { SponsorshipRequest, type SponsorableEntityType } from '../../domain/entities/SponsorshipRequest'; import type { SponsorshipTier } from '../../domain/entities/SeasonSponsorship'; import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository'; import type { ISponsorshipPricingRepository } from '../../domain/repositories/ISponsorshipPricingRepository'; import type { ISponsorRepository } from '../../domain/repositories/ISponsorRepository'; import { Money, type Currency } from '../../domain/value-objects/Money'; import type { AsyncUseCase } from '@gridpilot/shared/application'; import { EntityNotFoundError, BusinessRuleViolationError, } from '../errors/RacingApplicationError'; export interface ApplyForSponsorshipDTO { sponsorId: string; entityType: SponsorableEntityType; entityId: string; tier: SponsorshipTier; offeredAmount: number; // in cents currency?: Currency; message?: string; } export interface ApplyForSponsorshipResultDTO { requestId: string; status: 'pending'; createdAt: Date; } export class ApplyForSponsorshipUseCase implements AsyncUseCase { constructor( private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository, private readonly sponsorshipPricingRepo: ISponsorshipPricingRepository, private readonly sponsorRepo: ISponsorRepository, ) {} async execute(dto: ApplyForSponsorshipDTO): Promise { // Validate sponsor exists const sponsor = await this.sponsorRepo.findById(dto.sponsorId); if (!sponsor) { throw new EntityNotFoundError({ entity: 'sponsor', id: dto.sponsorId }); } // Check if entity accepts sponsorship applications const pricing = await this.sponsorshipPricingRepo.findByEntity(dto.entityType, dto.entityId); if (!pricing) { throw new BusinessRuleViolationError('This entity has not set up sponsorship pricing'); } if (!pricing.acceptingApplications) { throw new BusinessRuleViolationError( 'This entity is not currently accepting sponsorship applications', ); } // Check if the requested tier slot is available const slotAvailable = pricing.isSlotAvailable(dto.tier); if (!slotAvailable) { throw new BusinessRuleViolationError( `No ${dto.tier} sponsorship slots are available`, ); } // Check if sponsor already has a pending request for this entity const hasPending = await this.sponsorshipRequestRepo.hasPendingRequest( dto.sponsorId, dto.entityType, dto.entityId, ); if (hasPending) { throw new BusinessRuleViolationError( 'You already have a pending sponsorship request for this entity', ); } // Validate offered amount meets minimum price const minPrice = pricing.getPrice(dto.tier); if (minPrice && dto.offeredAmount < minPrice.amount) { throw new BusinessRuleViolationError( `Offered amount must be at least ${minPrice.format()}`, ); } // Create the sponsorship request const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const offeredAmount = Money.create(dto.offeredAmount, dto.currency ?? 'USD'); const request = SponsorshipRequest.create({ id: requestId, sponsorId: dto.sponsorId, entityType: dto.entityType, entityId: dto.entityId, tier: dto.tier, offeredAmount, ...(dto.message !== undefined ? { message: dto.message } : {}), }); await this.sponsorshipRequestRepo.create(request); return { requestId: request.id, status: 'pending', createdAt: request.createdAt, }; } }