/** * Application Use Case: GetEntitySponsorshipPricingUseCase * * Retrieves sponsorship pricing configuration for any entity. * Used by sponsors to see available slots and prices. */ import type { ISponsorshipPricingRepository } from '../../domain/repositories/ISponsorshipPricingRepository'; import type { ISponsorshipRequestRepository } from '../../domain/repositories/ISponsorshipRequestRepository'; import type { ISeasonSponsorshipRepository } from '../../domain/repositories/ISeasonSponsorshipRepository'; import type { SponsorableEntityType } from '../../domain/entities/SponsorshipRequest'; import type { SponsorshipTier } from '../../domain/entities/SeasonSponsorship'; import type { IEntitySponsorshipPricingPresenter } from '../presenters/IEntitySponsorshipPricingPresenter'; import type { AsyncUseCase } from '@gridpilot/shared/application'; export interface GetEntitySponsorshipPricingDTO { entityType: SponsorableEntityType; entityId: string; } export interface SponsorshipSlotDTO { tier: SponsorshipTier; price: number; currency: string; formattedPrice: string; benefits: string[]; available: boolean; maxSlots: number; filledSlots: number; pendingRequests: number; } export interface GetEntitySponsorshipPricingResultDTO { entityType: SponsorableEntityType; entityId: string; acceptingApplications: boolean; customRequirements?: string; mainSlot?: SponsorshipSlotDTO; secondarySlot?: SponsorshipSlotDTO; } export class GetEntitySponsorshipPricingUseCase implements AsyncUseCase { constructor( private readonly sponsorshipPricingRepo: ISponsorshipPricingRepository, private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository, private readonly seasonSponsorshipRepo: ISeasonSponsorshipRepository, private readonly presenter: IEntitySponsorshipPricingPresenter, ) {} async execute(dto: GetEntitySponsorshipPricingDTO): Promise { const pricing = await this.sponsorshipPricingRepo.findByEntity(dto.entityType, dto.entityId); if (!pricing) { this.presenter.present(null); return; } // Count pending requests by tier const pendingRequests = await this.sponsorshipRequestRepo.findPendingByEntity( dto.entityType, dto.entityId ); const pendingMainCount = pendingRequests.filter(r => r.tier === 'main').length; const pendingSecondaryCount = pendingRequests.filter(r => r.tier === 'secondary').length; // Count filled slots (for seasons, check SeasonSponsorship table) let filledMainSlots = 0; let filledSecondarySlots = 0; if (dto.entityType === 'season') { const sponsorships = await this.seasonSponsorshipRepo.findBySeasonId(dto.entityId); const activeSponsorships = sponsorships.filter(s => s.isActive()); filledMainSlots = activeSponsorships.filter(s => s.tier === 'main').length; filledSecondarySlots = activeSponsorships.filter(s => s.tier === 'secondary').length; } const result: GetEntitySponsorshipPricingResultDTO = { entityType: dto.entityType, entityId: dto.entityId, acceptingApplications: pricing.acceptingApplications, ...(pricing.customRequirements !== undefined ? { customRequirements: pricing.customRequirements } : {}), }; if (pricing.mainSlot) { const mainMaxSlots = pricing.mainSlot.maxSlots; result.mainSlot = { tier: 'main', price: pricing.mainSlot.price.amount, currency: pricing.mainSlot.price.currency, formattedPrice: pricing.mainSlot.price.format(), benefits: pricing.mainSlot.benefits, available: pricing.mainSlot.available && filledMainSlots < mainMaxSlots, maxSlots: mainMaxSlots, filledSlots: filledMainSlots, pendingRequests: pendingMainCount, }; } if (pricing.secondarySlots) { const secondaryMaxSlots = pricing.secondarySlots.maxSlots; result.secondarySlot = { tier: 'secondary', price: pricing.secondarySlots.price.amount, currency: pricing.secondarySlots.price.currency, formattedPrice: pricing.secondarySlots.price.format(), benefits: pricing.secondarySlots.benefits, available: pricing.secondarySlots.available && filledSecondarySlots < secondaryMaxSlots, maxSlots: secondaryMaxSlots, filledSlots: filledSecondarySlots, pendingRequests: pendingSecondaryCount, }; } this.presenter.present(result); } }