/** * Query: GetEntitySponsorshipPricingQuery * * 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'; 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 GetEntitySponsorshipPricingQuery { constructor( private readonly sponsorshipPricingRepo: ISponsorshipPricingRepository, private readonly sponsorshipRequestRepo: ISponsorshipRequestRepository, private readonly seasonSponsorshipRepo: ISeasonSponsorshipRepository, ) {} async execute(dto: GetEntitySponsorshipPricingDTO): Promise { const pricing = await this.sponsorshipPricingRepo.findByEntity(dto.entityType, dto.entityId); if (!pricing) { return null; } // 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, 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, }; } return result; } }