import { ProtestsApiClient } from '../../api/protests/ProtestsApiClient';
import { ProtestViewModel } from '../../view-models/ProtestViewModel';
import { RaceViewModel } from '../../view-models/RaceViewModel';
import { ProtestDriverViewModel } from '../../view-models/ProtestDriverViewModel';
import type { LeagueAdminProtestsDTO } from '../../types/generated/LeagueAdminProtestsDTO';
import type { ApplyPenaltyCommandDTO } from '../../types/generated/ApplyPenaltyCommandDTO';
import type { RequestProtestDefenseCommandDTO } from '../../types/generated/RequestProtestDefenseCommandDTO';
import type { ReviewProtestCommandDTO } from '../../types/generated/ReviewProtestCommandDTO';
import type { DriverDTO } from '../../types/generated/DriverDTO';

/**
 * Protest Service
 *
 * Orchestrates protest operations by coordinating API calls and view model creation.
 * All dependencies are injected via constructor.
 */
export class ProtestService {
  constructor(
    private readonly apiClient: ProtestsApiClient
  ) {}

  /**
   * Get protests for a league with view model transformation
   */
  async getLeagueProtests(leagueId: string): Promise<{
    protests: ProtestViewModel[];
    racesById: LeagueAdminProtestsDTO['racesById'];
    driversById: LeagueAdminProtestsDTO['driversById'];
  }> {
    const dto = await this.apiClient.getLeagueProtests(leagueId);
    return {
      protests: dto.protests.map(protest => new ProtestViewModel(protest)),
      racesById: dto.racesById,
      driversById: dto.driversById,
    };
  }

  /**
   * Get a single protest by ID from league protests
   */
  async getProtestById(leagueId: string, protestId: string): Promise<{
    protest: ProtestViewModel;
    race: RaceViewModel;
    protestingDriver: ProtestDriverViewModel;
    accusedDriver: ProtestDriverViewModel;
  } | null> {
    const dto = await this.apiClient.getLeagueProtest(leagueId, protestId);
    const protest = dto.protests[0];
    if (!protest) return null;

    const race = Object.values(dto.racesById)[0];
    if (!race) return null;

    // Cast to the correct type for indexing
    const driversById = dto.driversById as unknown as Record<string, DriverDTO>;
    const protestingDriver = driversById[protest.protestingDriverId];
    const accusedDriver = driversById[protest.accusedDriverId];

    if (!protestingDriver || !accusedDriver) return null;

    return {
      protest: new ProtestViewModel(protest),
      race: new RaceViewModel(race),
      protestingDriver: new ProtestDriverViewModel(protestingDriver),
      accusedDriver: new ProtestDriverViewModel(accusedDriver),
    };
  }

  /**
   * Apply a penalty
   */
  async applyPenalty(input: ApplyPenaltyCommandDTO): Promise<void> {
    await this.apiClient.applyPenalty(input);
  }

  /**
   * Request protest defense
   */
  async requestDefense(input: RequestProtestDefenseCommandDTO): Promise<void> {
    await this.apiClient.requestDefense(input);
  }

  /**
   * Review protest
   */
  async reviewProtest(input: { protestId: string; stewardId: string; decision: string; decisionNotes: string }): Promise<void> {
    const normalizedDecision =
      input.decision.toLowerCase() === 'upheld' ? 'uphold' : input.decision.toLowerCase();

    const command: ReviewProtestCommandDTO = {
      protestId: input.protestId,
      stewardId: input.stewardId,
      decision: normalizedDecision,
      decisionNotes: input.decisionNotes,
    };

    await this.apiClient.reviewProtest(command);
  }

  /**
   * Find protests by race ID
   */
  async findByRaceId(raceId: string): Promise<any[]> {
    const dto = await this.apiClient.getRaceProtests(raceId);
    return dto.protests;
  }
}