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

import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { ChampionshipStandingRepository } from '../../domain/repositories/ChampionshipStandingRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { LeagueScoringConfigRepository } from '../../domain/repositories/LeagueScoringConfigRepository';
import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';

export type RecalculateChampionshipStandingsInput = {
  leagueId: string;
  seasonId: string;
};

export type ChampionshipStandingsEntry = {
  driverId: string | null;
  teamId: string | null;
  position: number;
  points: number;
};

export type RecalculateChampionshipStandingsResult = {
  leagueId: string;
  seasonId: string;
  entries: ChampionshipStandingsEntry[];
};

export type RecalculateChampionshipStandingsErrorCode =
  | 'LEAGUE_NOT_FOUND'
  | 'SEASON_NOT_FOUND'
  | 'REPOSITORY_ERROR';

export class RecalculateChampionshipStandingsUseCase {
  constructor(private readonly leagueRepository: LeagueRepository,
    private readonly seasonRepository: SeasonRepository,
    private readonly leagueScoringConfigRepository: LeagueScoringConfigRepository,
    private readonly raceRepository: RaceRepository,
    private readonly resultRepository: ResultRepository,
    private readonly penaltyRepository: PenaltyRepository,
    private readonly championshipStandingRepository: ChampionshipStandingRepository,
    private readonly eventScoringService: EventScoringService,
    private readonly championshipAggregator: ChampionshipAggregator,
    private readonly logger: Logger) {}

  async execute(
    input: RecalculateChampionshipStandingsInput,
  ): Promise<
    Result<
      RecalculateChampionshipStandingsResult,
      ApplicationErrorCode<RecalculateChampionshipStandingsErrorCode, { message: string }>
    >
  > {
    const { leagueId, seasonId } = input;

    try {
      const league = await this.leagueRepository.findById(leagueId);
      if (!league) {
        return Result.err({
          code: 'LEAGUE_NOT_FOUND',
          details: { message: `League not found: ${leagueId}` },
        });
      }

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

      const leagueScoringConfig =
        await this.leagueScoringConfigRepository.findBySeasonId(seasonId);
      if (!leagueScoringConfig) {
        throw new Error(`League scoring config not found for season: ${seasonId}`);
      }

      const championship = this.findChampionshipConfig(leagueScoringConfig.championships);

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

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

      for (const race of races) {
        const sessionType = this.mapRaceSessionType(String(race.sessionType));
        if (!championship.sessionTypes.includes(sessionType)) {
          continue;
        }

        const results = await this.resultRepository.findByRaceId(race.id);
        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 result: RecalculateChampionshipStandingsResult = {
        leagueId,
        seasonId,
        entries: standings.map((standing) => ({
          driverId: standing.participant?.id ?? null,
          teamId: null,
          position: standing.position.toNumber(),
          points: standing.totalPoints.toNumber(),
        })),
      };

      return Result.ok(result);
    } catch (error) {
      const err = error as Error;
      this.logger.error('Failed to recalculate championship standings', err, {
        leagueId,
        seasonId,
      });

      return Result.err({
        code: 'REPOSITORY_ERROR',
        details: {
          message:
            err.message || 'Failed to recalculate championship standings',
        },
      });
    }
  }

  private findChampionshipConfig(championships: ChampionshipConfig[]): ChampionshipConfig {
    if (!championships || championships.length === 0) {
      throw new Error('No championship configurations found');
    }

    return championships[0]!;
  }

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