import type { ISeasonRepository } from '@core/racing/domain/repositories/ISeasonRepository';
import type { ILeagueScoringConfigRepository } from '@core/racing/domain/repositories/ILeagueScoringConfigRepository';
import type { IRaceRepository } from '@core/racing/domain/repositories/IRaceRepository';
import type { IResultRepository } from '@core/racing/domain/repositories/IResultRepository';
import type { IPenaltyRepository } from '@core/racing/domain/repositories/IPenaltyRepository';
import type { IChampionshipStandingRepository } from '@core/racing/domain/repositories/IChampionshipStandingRepository';

import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
import type { SessionType } from '@core/racing/domain/types/SessionType';
import type { ChampionshipStanding } from '@core/racing/domain/entities/championship/ChampionshipStanding';
import { EventScoringService } from '@core/racing/domain/services/EventScoringService';
import { ChampionshipAggregator } from '@core/racing/domain/services/ChampionshipAggregator';

import type {
  ChampionshipStandingsDTO,
  ChampionshipStandingsRowDTO,
} from '../dto/ChampionshipStandingsDTO';

import type { AsyncUseCase } from '@core/shared/application/AsyncUseCase';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

export interface RecalculateChampionshipStandingsParams {
  seasonId: string;
  championshipId: string;
}

type RecalculateChampionshipStandingsErrorCode =
  | 'SEASON_NOT_FOUND'
  | 'LEAGUE_SCORING_CONFIG_NOT_FOUND'
  | 'CHAMPIONSHIP_CONFIG_NOT_FOUND';

export class RecalculateChampionshipStandingsUseCase
  implements AsyncUseCase<RecalculateChampionshipStandingsParams, ChampionshipStandingsDTO, RecalculateChampionshipStandingsErrorCode>
{
  constructor(
    private readonly seasonRepository: ISeasonRepository,
    private readonly leagueScoringConfigRepository: ILeagueScoringConfigRepository,
    private readonly raceRepository: IRaceRepository,
    private readonly resultRepository: IResultRepository,
    private readonly penaltyRepository: IPenaltyRepository,
    private readonly championshipStandingRepository: IChampionshipStandingRepository,
    private readonly eventScoringService: EventScoringService,
    private readonly championshipAggregator: ChampionshipAggregator,
  ) {}

  async execute(params: RecalculateChampionshipStandingsParams): Promise<Result<ChampionshipStandingsDTO, ApplicationErrorCode<RecalculateChampionshipStandingsErrorCode>>> {
    const { seasonId, championshipId } = params;

    const season = await this.seasonRepository.findById(seasonId);
    if (!season) {
      return Result.err({ code: 'SEASON_NOT_FOUND', details: { message: `Season not found: ${seasonId}` } });
    }

    const leagueScoringConfig =
      await this.leagueScoringConfigRepository.findBySeasonId(seasonId);
    if (!leagueScoringConfig) {
      return Result.err({ code: 'LEAGUE_SCORING_CONFIG_NOT_FOUND', details: { message: `League scoring config not found for season: ${seasonId}` } });
    }

    const championship = leagueScoringConfig.championships.find((c) => c.id === championshipId);
    if (!championship) {
      return Result.err({ code: 'CHAMPIONSHIP_CONFIG_NOT_FOUND', details: { message: `Championship config not found: ${championshipId}` } });
    }

    const races = await this.raceRepository.findByLeagueId(season.leagueId);

    const eventPointsByEventId: Record<string, ReturnType<EventScoringService['scoreSession']>> =
      {};

    for (const race of races) {
      // Map existing Race.sessionType into scoring SessionType where possible.
      const sessionType = this.mapRaceSessionType(race.sessionType);
      if (!championship.sessionTypes.includes(sessionType)) {
        continue;
      }

      const results = await this.resultRepository.findByRaceId(race.id);

      // Fetch penalties for this specific race
      const penalties = await this.penaltyRepository.findByRaceId(race.id);

      const participantPoints = this.eventScoringService.scoreSession({
        seasonId,
        championship,
        sessionType,
        results,
        penalties,
      });

      eventPointsByEventId[race.id] = participantPoints;
    }

    const standings: ChampionshipStanding[] = this.championshipAggregator.aggregate({
      seasonId,
      championship,
      eventPointsByEventId,
    });

    await this.championshipStandingRepository.saveAll(standings);

    const rows: ChampionshipStandingsRowDTO[] = standings.map((s) => ({
      participant: s.participant,
      position: s.position.toNumber(),
      totalPoints: s.totalPoints.toNumber(),
      resultsCounted: s.resultsCounted.toNumber(),
      resultsDropped: s.resultsDropped.toNumber(),
    }));

    const dto: ChampionshipStandingsDTO = {
      seasonId,
      championshipId: championship.id,
      championshipName: championship.name,
      rows,
    };

    return Result.ok(dto);
  }


  private mapRaceSessionType(sessionType: string): SessionType {
    if (sessionType === 'race') {
      return 'main';
    }
    if (
      sessionType === 'practice' ||
      sessionType === 'qualifying' ||
      sessionType === 'timeTrial'
    ) {
      return sessionType;
    }
    return 'main';
  }
}