import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { IStandingRepository } from '../../domain/repositories/IStandingRepository';
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
import { Result } from '../../domain/entities/Result';
import { Standing } from '../../domain/entities/Standing';
import { RaceResultGenerator } from '../utils/RaceResultGenerator';
import { RatingUpdateService } from '@core/identity/domain/services/RatingUpdateService';
import type { AsyncUseCase } from '@core/shared/application';
import { Result as SharedResult } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { CompleteRaceCommandDTO } from '../dto/CompleteRaceCommandDTO';

/**
 * Enhanced CompleteRaceUseCase that includes rating updates
 */
export class CompleteRaceUseCaseWithRatings
  implements AsyncUseCase<CompleteRaceCommandDTO, void, string> {
  constructor(
    private readonly raceRepository: IRaceRepository,
    private readonly raceRegistrationRepository: IRaceRegistrationRepository,
    private readonly resultRepository: IResultRepository,
    private readonly standingRepository: IStandingRepository,
    private readonly driverRatingProvider: DriverRatingProvider,
    private readonly ratingUpdateService: RatingUpdateService,
  ) {}

  async execute(command: CompleteRaceCommandDTO): Promise<SharedResult<void, ApplicationErrorCode<string>>> {
    try {
      const { raceId } = command;

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

      // Get registered drivers for this race
      const registeredDriverIds = await this.raceRegistrationRepository.getRegisteredDrivers(raceId);
      if (registeredDriverIds.length === 0) {
        return SharedResult.err({ code: 'NO_REGISTERED_DRIVERS' });
      }

      // Get driver ratings
      const driverRatings = this.driverRatingProvider.getRatings(registeredDriverIds);

      // Generate realistic race results
      const results = RaceResultGenerator.generateRaceResults(raceId, registeredDriverIds, driverRatings);

      // Save results
      for (const result of results) {
        await this.resultRepository.create(result);
      }

      // Update standings
      await this.updateStandings(race.leagueId, results);

      // Update driver ratings based on performance
      await this.updateDriverRatings(results, registeredDriverIds.length);

      // Complete the race
      const completedRace = race.complete();
      await this.raceRepository.update(completedRace);

      return SharedResult.ok(undefined);
    } catch {
      return SharedResult.err({ code: 'UNKNOWN_ERROR' });
    }
  }

  private async updateStandings(leagueId: string, results: Result[]): Promise<void> {
    // Group results by driver
    const resultsByDriver = new Map<string, Result[]>();
    for (const result of results) {
      const existing = resultsByDriver.get(result.driverId) || [];
      existing.push(result);
      resultsByDriver.set(result.driverId, existing);
    }

    // Update or create standings for each driver
    for (const [driverId, driverResults] of resultsByDriver) {
      let standing = await this.standingRepository.findByDriverIdAndLeagueId(driverId, leagueId);

      if (!standing) {
        standing = Standing.create({
          leagueId,
          driverId,
        });
      }

      // Add all results for this driver (should be just one for this race)
      for (const result of driverResults) {
        standing = standing.addRaceResult(result.position, {
          1: 25, 2: 18, 3: 15, 4: 12, 5: 10, 6: 8, 7: 6, 8: 4, 9: 2, 10: 1
        });
      }

      await this.standingRepository.save(standing);
    }
  }

  private async updateDriverRatings(results: Result[], totalDrivers: number): Promise<void> {
    const driverResults = results.map(result => ({
      driverId: result.driverId,
      position: result.position,
      totalDrivers,
      incidents: result.incidents,
      startPosition: result.startPosition,
    }));

    await this.ratingUpdateService.updateDriverRatingsAfterRace(driverResults);
  }
}
