/** * Application Use Case: ReviewProtestUseCase * * Allows a steward to review a protest and make a decision (uphold or dismiss). */ import type { Logger } from '@core/shared/domain/Logger'; import { Result } from '@core/shared/domain/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository'; import { ProtestRepository } from '../../domain/repositories/ProtestRepository'; import { RaceRepository } from '../../domain/repositories/RaceRepository'; import { LeagueMembership } from '../../domain/entities/LeagueMembership'; export type ReviewProtestErrorCode = 'PROTEST_NOT_FOUND' | 'RACE_NOT_FOUND' | 'NOT_LEAGUE_ADMIN' | 'REPOSITORY_ERROR'; export type ReviewProtestApplicationError = ApplicationErrorCode; export interface ReviewProtestInput { protestId: string; stewardId: string; decision: 'uphold' | 'dismiss'; decisionNotes: string; } export interface ReviewProtestResult { leagueId: string; protestId: string; status: 'upheld' | 'dismissed'; } export class ReviewProtestUseCase { constructor( private readonly protestRepository: ProtestRepository, private readonly raceRepository: RaceRepository, private readonly leagueMembershipRepository: LeagueMembershipRepository, private readonly logger: Logger, ) {} async execute(input: ReviewProtestInput): Promise> { this.logger.debug('Executing ReviewProtestUseCase', { input }); try { // Load the protest const protest = await this.protestRepository.findById(input.protestId); if (!protest) { this.logger.warn('Protest not found', { protestId: input.protestId }); return Result.err({ code: 'PROTEST_NOT_FOUND', details: { message: 'Protest not found' } }); } // Load the race to get league ID const race = await this.raceRepository.findById(protest.raceId); if (!race) { this.logger.warn('Race not found for protest', { protestId: input.protestId, raceId: protest.raceId }); return Result.err({ code: 'RACE_NOT_FOUND', details: { message: 'Race not found' } }); } // Validate steward has authority (owner or admin of the league) const memberships = await this.leagueMembershipRepository.getLeagueMembers(race.leagueId); const stewardMembership = memberships.find( (m: LeagueMembership) => m.driverId.toString() === input.stewardId && m.status.toString() === 'active' ); if (!stewardMembership || (stewardMembership.role.toString() !== 'owner' && stewardMembership.role.toString() !== 'admin')) { this.logger.warn('Unauthorized steward attempting to review protest', { stewardId: input.stewardId, leagueId: race.leagueId }); return Result.err({ code: 'NOT_LEAGUE_ADMIN', details: { message: 'Only league owners and admins can review protests' } }); } // Apply the decision const updatedProtest = input.decision === 'uphold' ? protest.uphold(input.stewardId, input.decisionNotes) : protest.dismiss(input.stewardId, input.decisionNotes); await this.protestRepository.update(updatedProtest); const protestId = (() => { const unknownId = (protest as unknown as { id: unknown }).id; if (typeof unknownId === 'string') return unknownId; if ( unknownId && typeof unknownId === 'object' && 'toString' in unknownId && typeof (unknownId as { toString: unknown }).toString === 'function' ) { return (unknownId as { toString: () => string }).toString(); } return String(unknownId); })(); const result: ReviewProtestResult = { leagueId: race.leagueId, protestId, status: input.decision === 'uphold' ? 'upheld' : 'dismissed', }; this.logger.info('Protest reviewed successfully', { protestId: result.protestId, leagueId: result.leagueId, status: result.status, }); return Result.ok(result); } catch (error) { const message = error instanceof Error ? error.message : 'Failed to review protest'; this.logger.error('Failed to review protest', new Error(message)); return Result.err({ code: 'REPOSITORY_ERROR', details: { message } }); } } }