import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
import type { StandingRepository } from '../../domain/repositories/StandingRepository';
import type { DriverRatingPort } from '../ports/DriverRatingPort';

export type DriverSeasonStats = {
  leagueId: string;
  driverId: string;
  position: number;
  driverName: string;
  teamId: string | undefined;
  teamName: string | undefined;
  totalPoints: number;
  basePoints: number;
  penaltyPoints: number;
  bonusPoints: number;
  pointsPerRace: number;
  racesStarted: number;
  racesFinished: number;
  dnfs: number;
  noShows: number;
  avgFinish: number | null;
  rating: number | null;
  ratingChange: number | null;
};

export type GetLeagueDriverSeasonStatsInput = {
  leagueId: string;
};

export type GetLeagueDriverSeasonStatsResult = {
  leagueId: string;
  stats: DriverSeasonStats[];
};

export type GetLeagueDriverSeasonStatsErrorCode =
  | 'LEAGUE_NOT_FOUND'
  | 'SEASON_NOT_FOUND'
  | 'DRIVER_NOT_FOUND'
  | 'REPOSITORY_ERROR';

/**
 * Use Case for retrieving league driver season statistics.
 * Orchestrates domain logic and returns the result.
 */
export class GetLeagueDriverSeasonStatsUseCase {
  constructor(private readonly standingRepository: StandingRepository,
    private readonly resultRepository: ResultRepository,
    private readonly penaltyRepository: PenaltyRepository,
    private readonly raceRepository: RaceRepository,
    private readonly driverRepository: DriverRepository,
    private readonly driverRatingPort: DriverRatingPort) {}

  async execute(
    input: GetLeagueDriverSeasonStatsInput,
  ): Promise<
    Result<GetLeagueDriverSeasonStatsResult, ApplicationErrorCode<GetLeagueDriverSeasonStatsErrorCode, { message: string }>>
  > {
    try {
      const { leagueId } = input;

      const [standings, races] = await Promise.all([
        this.standingRepository.findByLeagueId(leagueId),
        this.raceRepository.findByLeagueId(leagueId),
      ]);

      if (!standings || standings.length === 0) {
        return Result.err({
          code: 'LEAGUE_NOT_FOUND',
          details: { message: 'League not found' },
        });
      }

      const penaltiesArrays = await Promise.all(
        races.map(race => this.penaltyRepository.findByRaceId(race.id)),
      );
      const penaltiesForLeague = penaltiesArrays.flat();

      const penaltiesByDriver = new Map<string, { baseDelta: number; bonusDelta: number }>();
      for (const p of penaltiesForLeague) {
        if (p.status !== 'applied') continue;

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

        if (p.type === 'points_deduction' && p.value) {
          current.baseDelta -= p.value;
        }

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

      const driverRatings = new Map<string, { rating: number | null; ratingChange: number | null }>();
      for (const standing of standings) {
        const driverId = standing.driverId.toString();
        const rating = await this.driverRatingPort.getDriverRating(driverId);
        driverRatings.set(driverId, { rating, ratingChange: null });
      }

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

      const driverIds = standings.map(s => s.driverId.toString());
      const drivers = await Promise.all(driverIds.map(id => this.driverRepository.findById(id)));
      const driversMap = new Map(
        drivers
          .filter((driver): driver is NonNullable<typeof driver> => driver !== null)
          .map(driver => [driver.id, driver]),
      );

      const stats: DriverSeasonStats[] = standings.map(standing => {
        const driverId = standing.driverId.toString();
        const driver = driversMap.get(driverId);
        const penalties = penaltiesByDriver.get(driverId) ?? { baseDelta: 0, bonusDelta: 0 };
        const results = driverResults.get(driverId) ?? [];
        const rating = driverRatings.get(driverId);

        const racesStarted = results.length;
        const racesFinished = results.filter(r => r.position > 0).length;
        const dnfs = results.filter(r => r.position === 0).length;
        const noShows = races.length - racesStarted;
        const avgFinish =
          results.length > 0
            ? results.reduce((sum, r) => sum + r.position, 0) / results.length
            : null;
        const totalPoints = standing.points.toNumber();
        const pointsPerRace = racesStarted > 0 ? totalPoints / racesStarted : 0;

        return {
          leagueId,
          driverId,
          position: standing.position.toNumber(),
          driverName: driver ? driver.name.toString() : '',
          teamId: undefined,
          teamName: undefined,
          totalPoints,
          basePoints: totalPoints - penalties.baseDelta,
          penaltyPoints: penalties.baseDelta,
          bonusPoints: penalties.bonusDelta,
          pointsPerRace,
          racesStarted,
          racesFinished,
          dnfs,
          noShows,
          avgFinish,
          rating: rating?.rating ?? null,
          ratingChange: rating?.ratingChange ?? null,
        };
      });

      const result: GetLeagueDriverSeasonStatsResult = {
        leagueId,
        stats,
      };

      return Result.ok(result);
    } catch (error) {
      const message =
        error instanceof Error && error.message
          ? error.message
          : 'Failed to fetch league driver season stats';

      return Result.err({
        code: 'REPOSITORY_ERROR',
        details: { message },
      });
    }
  }
}
