/**
 * Use Case: GetRaceWithSOFUseCase
 *
 * Returns race details enriched with calculated Strength of Field (SOF).
 * SOF is calculated from participant ratings if not already stored on the race.
 */

import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { AsyncUseCase } from '@core/shared/application/AsyncUseCase';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
import {
  AverageStrengthOfFieldCalculator,
  type StrengthOfFieldCalculator,
} from '../../domain/services/StrengthOfFieldCalculator';

export interface GetRaceWithSOFQueryParams {
  raceId: string;
}

export interface RaceWithSOFResultDTO {
  raceId: string;
  leagueId: string;
  scheduledAt: Date;
  track: string;
  trackId: string;
  car: string;
  carId: string;
  sessionType: string;
  status: string;
  strengthOfField: number | null;
  registeredCount: number;
  maxParticipants: number;
  participantCount: number;
}

type GetRaceWithSOFErrorCode = 'RACE_NOT_FOUND';

export class GetRaceWithSOFUseCase implements AsyncUseCase<GetRaceWithSOFQueryParams, RaceWithSOFResultDTO, GetRaceWithSOFErrorCode> {
  private readonly sofCalculator: StrengthOfFieldCalculator;

  constructor(
    private readonly raceRepository: IRaceRepository,
    private readonly registrationRepository: IRaceRegistrationRepository,
    private readonly resultRepository: IResultRepository,
    private readonly driverRatingProvider: DriverRatingProvider,
    sofCalculator?: StrengthOfFieldCalculator,
  ) {
    this.sofCalculator = sofCalculator ?? new AverageStrengthOfFieldCalculator();
  }

  async execute(params: GetRaceWithSOFQueryParams): Promise<Result<RaceWithSOFResultDTO, ApplicationErrorCode<GetRaceWithSOFErrorCode>>> {
    const { raceId } = params;

    const race = await this.raceRepository.findById(raceId);
    if (!race) {
      return Result.err({ code: 'RACE_NOT_FOUND' });
    }

    // Get participant IDs based on race status
    let participantIds: string[] = [];

    if (race.status === 'completed') {
      // For completed races, use results
      const results = await this.resultRepository.findByRaceId(raceId);
      participantIds = results.map(r => r.driverId);
    } else {
      // For upcoming/running races, use registrations
      participantIds = await this.registrationRepository.getRegisteredDrivers(raceId);
    }

    // Use stored SOF if available, otherwise calculate
    let strengthOfField = race.strengthOfField ?? null;

    if (strengthOfField === null && participantIds.length > 0) {
      const ratings = this.driverRatingProvider.getRatings(participantIds);
      const driverRatings = participantIds
        .filter(id => ratings.has(id))
        .map(id => ({ driverId: id, rating: ratings.get(id)! }));

      strengthOfField = this.sofCalculator.calculate(driverRatings);
    }

    const dto: RaceWithSOFResultDTO = {
      raceId: race.id,
      leagueId: race.leagueId,
      scheduledAt: race.scheduledAt,
      track: race.track ?? '',
      trackId: race.trackId ?? '',
      car: race.car ?? '',
      carId: race.carId ?? '',
      sessionType: race.sessionType.props,
      status: race.status,
      strengthOfField,
      registeredCount: race.registeredCount ?? participantIds.length,
      maxParticipants: race.maxParticipants ?? participantIds.length,
      participantCount: participantIds.length,
    };

    return Result.ok(dto);
  }
}