import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { IProtestRepository } from '../../domain/repositories/IProtestRepository';
import type { IDriverRepository } from '../../domain/repositories/IDriverRepository';
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Result } from '@core/shared/application/Result';
import type { UseCaseOutputPort } from '@core/shared/application';
import type { Race } from '../../domain/entities/Race';
import type { Protest } from '../../domain/entities/Protest';
import type { Driver } from '../../domain/entities/Driver';
import type { League } from '../../domain/entities/League';

export interface GetLeagueProtestsInput {
  leagueId: string;
}

export type GetLeagueProtestsErrorCode = 'LEAGUE_NOT_FOUND' | 'REPOSITORY_ERROR';

export interface LeagueProtestWithEntities {
  protest: Protest;
  race: Race | null;
  protestingDriver: Driver | null;
  accusedDriver: Driver | null;
}

export interface GetLeagueProtestsResult {
  league: League;
  protests: LeagueProtestWithEntities[];
}

export class GetLeagueProtestsUseCase {
  constructor(
    private readonly raceRepository: IRaceRepository,
    private readonly protestRepository: IProtestRepository,
    private readonly driverRepository: IDriverRepository,
    private readonly leagueRepository: ILeagueRepository,
    private readonly output: UseCaseOutputPort<GetLeagueProtestsResult>,
  ) {}

  get outputPort(): UseCaseOutputPort<GetLeagueProtestsResult> {
    return this.output;
  }

  async execute(
    input: GetLeagueProtestsInput,
  ): Promise<Result<void, ApplicationErrorCode<GetLeagueProtestsErrorCode, { message: string }>>> {
    try {
      const league = await this.leagueRepository.findById(input.leagueId);

      if (!league) {
        return Result.err({
          code: 'LEAGUE_NOT_FOUND',
          details: { message: 'League not found' },
        });
      }

      const races = await this.raceRepository.findByLeagueId(input.leagueId);
      const protests: Protest[] = [];
      const racesById: Record<string, Race> = {};
      const driversById: Record<string, Driver> = {};
      const driverIds = new Set<string>();

      for (const race of races) {
        racesById[race.id] = race;
        const raceProtests = await this.protestRepository.findByRaceId(race.id);
        for (const protest of raceProtests) {
          protests.push(protest);
          driverIds.add(protest.protestingDriverId);
          driverIds.add(protest.accusedDriverId);
        }
      }

      for (const driverId of driverIds) {
        const driver = await this.driverRepository.findById(driverId);
        if (driver) {
          driversById[driver.id] = driver;
        }
      }

      const protestsWithEntities: LeagueProtestWithEntities[] = protests.map(protest => ({
        protest,
        race: racesById[protest.raceId] ?? null,
        protestingDriver: driversById[protest.protestingDriverId] ?? null,
        accusedDriver: driversById[protest.accusedDriverId] ?? null,
      }));

      const result: GetLeagueProtestsResult = {
        league,
        protests: protestsWithEntities,
      };

      this.output.present(result);

      return Result.ok(undefined);
    } catch (error: unknown) {
      const message =
        error instanceof Error && error.message
          ? error.message
          : 'Failed to load league protests';

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