import { injectable, unmanaged } from 'inversify'; import { Result } from '@/lib/contracts/Result'; import { Service, type DomainError } from '@/lib/contracts/services/Service'; import { type StewardingApiDto } from '@/lib/types/tbd/StewardingApiDto'; import { RaceService } from '../races/RaceService'; import { ProtestService } from '../protests/ProtestService'; import { PenaltyService } from '../penalties/PenaltyService'; import { DriverService } from '../drivers/DriverService'; import { LeagueMembershipService } from './LeagueMembershipService'; import { LeagueStewardingViewModel } from '@/lib/view-models/LeagueStewardingViewModel'; @injectable() export class LeagueStewardingService implements Service { constructor( @unmanaged() private readonly raceService?: RaceService, @unmanaged() private readonly protestService?: ProtestService, @unmanaged() private readonly penaltyService?: PenaltyService, @unmanaged() private readonly driverService?: DriverService, @unmanaged() private readonly leagueMembershipService?: LeagueMembershipService, ) {} async getLeagueStewardingData(leagueId: string): Promise { if (!this.raceService || !this.protestService || !this.penaltyService || !this.driverService) { return new LeagueStewardingViewModel({ racesWithData: [], driverMap: {} }); } const racesRes = await this.raceService.findByLeagueId(leagueId); const races = (racesRes as any).value || racesRes; const racesWithData = await Promise.all( races.map(async (race: any) => { const [protestsRes, penaltiesRes] = await Promise.all([ this.protestService!.findByRaceId(race.id), this.penaltyService!.findByRaceId(race.id), ]); const protests = (protestsRes as any).value || protestsRes; const penalties = (penaltiesRes as any).value || penaltiesRes; return { race: { id: race.id, track: race.track, scheduledAt: new Date(race.scheduledAt), }, pendingProtests: protests.filter((p: any) => p.status === 'pending' || p.status === 'under_review'), resolvedProtests: protests.filter((p: any) => p.status !== 'pending' && p.status !== 'under_review'), penalties: penalties, }; }), ); const driverIds = new Set(); racesWithData.forEach((r: any) => { r.pendingProtests.forEach((p: any) => { driverIds.add(p.protestingDriverId); driverIds.add(p.accusedDriverId); }); r.resolvedProtests.forEach((p: any) => { driverIds.add(p.protestingDriverId); driverIds.add(p.accusedDriverId); }); r.penalties.forEach((p: any) => driverIds.add(p.driverId)); }); const driversRes = await this.driverService.findByIds(Array.from(driverIds)); const drivers = (driversRes as any).value || driversRes; const driverMap: Record = {}; drivers.forEach((d: any) => { driverMap[d.id] = d; }); return new LeagueStewardingViewModel({ racesWithData: racesWithData as any, driverMap }); } async reviewProtest(input: any): Promise { if (this.protestService) { await this.protestService.reviewProtest(input); } } async applyPenalty(input: any): Promise { if (this.penaltyService) { await this.penaltyService.applyPenalty(input); } } async getStewardingData(leagueId: string): Promise> { // Mock data since backend not implemented const mockData: StewardingApiDto = { leagueId, totalPending: 0, totalResolved: 0, totalPenalties: 0, races: [], drivers: [] }; return Result.ok(mockData); } async getProtestDetailViewModel(leagueId: string, protestId: string): Promise { if (!this.protestService || !this.penaltyService) return null; const [protestRes, penaltyTypesRes] = await Promise.all([ this.protestService.getProtestById(leagueId, protestId), this.penaltyService.getPenaltyTypesReference(), ]); const protestData = (protestRes as any).value || protestRes; const penaltyTypesData = (penaltyTypesRes as any).value || penaltyTypesRes; return { ...protestData, penaltyTypes: penaltyTypesData.penaltyTypes, defaultReasons: penaltyTypesData.defaultReasons, initialPenaltyType: penaltyTypesData.penaltyTypes[0]?.type, }; } }