import type { IStandingRepository } from '../../domain/repositories/IStandingRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { LeagueDriverSeasonStatsResultDTO } from '../presenters/ILeagueDriverSeasonStatsPresenter';
import type { AsyncUseCase } from '@core/shared/application';
import { Result } from '@core/shared/application/Result';
import type { DriverRatingPort } from '../ports/DriverRatingPort';

/**
 * Use Case for retrieving league driver season statistics.
 * Orchestrates domain logic and returns the result.
 */
export class GetLeagueDriverSeasonStatsUseCase implements AsyncUseCase<{ leagueId: string }, LeagueDriverSeasonStatsResultDTO, 'NO_ERROR'> {
  constructor(
    private readonly standingRepository: IStandingRepository,
    private readonly resultRepository: IResultRepository,
    private readonly penaltyRepository: IPenaltyRepository,
    private readonly raceRepository: IRaceRepository,
    private readonly driverRatingPort: DriverRatingPort,
  ) {}

  async execute(params: { leagueId: string }): Promise<Result<LeagueDriverSeasonStatsResultDTO, never>> {
    const { leagueId } = params;

    // Get standings and races for the league
    const [standings, races] = await Promise.all([
      this.standingRepository.findByLeagueId(leagueId),
      this.raceRepository.findByLeagueId(leagueId),
    ]);

    // Fetch all penalties for all races in the league
    const penaltiesArrays = await Promise.all(
      races.map(race => this.penaltyRepository.findByRaceId(race.id))
    );
    const penaltiesForLeague = penaltiesArrays.flat();

    // Group penalties by driver for quick lookup
    const penaltiesByDriver = new Map<string, { baseDelta: number; bonusDelta: number }>();
    for (const p of penaltiesForLeague) {
      // Only count applied penalties
      if (p.status !== 'applied') continue;

      const current = penaltiesByDriver.get(p.driverId) ?? { baseDelta: 0, bonusDelta: 0 };

      // Convert penalty to points delta based on type
      if (p.type === 'points_deduction' && p.value) {
        // Points deductions are negative
        current.baseDelta -= p.value;
      }

      penaltiesByDriver.set(p.driverId, current);
    }

    // Collect driver ratings
    const driverRatings = new Map<string, { rating: number | null; ratingChange: number | null }>();
    for (const standing of standings) {
      const ratingInfo = this.driverRatingPort.getRating(standing.driverId);
      driverRatings.set(standing.driverId, ratingInfo);
    }

    // Collect driver results
    const driverResults = new Map<string, Array<{ position: number }>>();
    for (const standing of standings) {
      const results = await this.resultRepository.findByDriverIdAndLeagueId(
        standing.driverId,
        leagueId,
      );
      driverResults.set(standing.driverId, results);
    }

    const dto: LeagueDriverSeasonStatsResultDTO = {
      leagueId,
      standings: standings.map(standing => ({
        driverId: standing.driverId,
        position: standing.position,
        points: standing.points,
        racesCompleted: standing.racesCompleted,
      })),
      penalties: penaltiesByDriver,
      driverResults,
      driverRatings,
    };

    return Result.ok(dto);
  }
}