import { RaceService } from '../races/RaceService';
import { ProtestService } from '../protests/ProtestService';
import { PenaltyService } from '../penalties/PenaltyService';
import { DriverService } from '../drivers/DriverService';
import { LeagueMembershipService } from './LeagueMembershipService';
import { LeagueStewardingViewModel, RaceWithProtests } from '../../view-models/LeagueStewardingViewModel';
import type { ProtestDetailViewModel } from '../../view-models/ProtestDetailViewModel';

/**
 * League Stewarding Service
 *
 * Orchestrates league stewarding operations by coordinating calls to race, protest, penalty, driver, and membership services.
 * All dependencies are injected via constructor.
 */
export class LeagueStewardingService {
  private getPenaltyValueLabel(valueKind: string): string {
    switch (valueKind) {
      case 'seconds':
        return 'seconds';
      case 'grid_positions':
        return 'positions';
      case 'points':
        return 'points';
      case 'races':
        return 'races';
      case 'none':
        return '';
      default:
        return '';
    }
  }

  private getFallbackDefaultPenaltyValue(valueKind: string): number {
    switch (valueKind) {
      case 'seconds':
        return 5;
      case 'grid_positions':
        return 3;
      case 'points':
        return 5;
      case 'races':
        return 1;
      case 'none':
        return 0;
      default:
        return 0;
    }
  }
  constructor(
    private readonly raceService: RaceService,
    private readonly protestService: ProtestService,
    private readonly penaltyService: PenaltyService,
    private readonly driverService: DriverService,
    private readonly leagueMembershipService: LeagueMembershipService
  ) {}

  /**
   * Get league stewarding data for all races in a league
   */
  async getLeagueStewardingData(leagueId: string): Promise<LeagueStewardingViewModel> {
    // Get all races for this league
    const leagueRaces = await this.raceService.findByLeagueId(leagueId);

    // Get protests and penalties for each race
    const protestsMap: Record<string, any[]> = {};
    const penaltiesMap: Record<string, any[]> = {};
    const driverIds = new Set<string>();

    for (const race of leagueRaces) {
      const raceProtests = await this.protestService.findByRaceId(race.id);
      const racePenalties = await this.penaltyService.findByRaceId(race.id);

      protestsMap[race.id] = raceProtests;
      penaltiesMap[race.id] = racePenalties;

      // Collect driver IDs
      raceProtests.forEach((p: any) => {
        driverIds.add(p.protestingDriverId);
        driverIds.add(p.accusedDriverId);
      });
      racePenalties.forEach((p: any) => {
        driverIds.add(p.driverId);
      });
    }

    // Load driver info
    const driverEntities = await this.driverService.findByIds(Array.from(driverIds));
    const driverMap: Record<string, any> = {};
    driverEntities.forEach((driver) => {
      if (driver) {
        driverMap[driver.id] = driver;
      }
    });

    // Compute race data with protest/penalty info
    const racesWithData: RaceWithProtests[] = leagueRaces.map(race => {
      const protests = protestsMap[race.id] || [];
      const penalties = penaltiesMap[race.id] || [];
      return {
        race: {
          id: race.id,
          track: race.track,
          scheduledAt: new Date(race.scheduledAt),
        },
        pendingProtests: protests.filter(p => p.status === 'pending' || p.status === 'under_review'),
        resolvedProtests: protests.filter(p => p.status === 'upheld' || p.status === 'dismissed' || p.status === 'withdrawn'),
        penalties
      };
    }).sort((a, b) => b.race.scheduledAt.getTime() - a.race.scheduledAt.getTime());

    return new LeagueStewardingViewModel(racesWithData, driverMap);
  }

  /**
   * Get protest review details as a page-ready view model
   */
  async getProtestDetailViewModel(leagueId: string, protestId: string): Promise<ProtestDetailViewModel> {
    const [protestData, penaltyTypesReference] = await Promise.all([
      this.protestService.getProtestById(leagueId, protestId),
      this.penaltyService.getPenaltyTypesReference(),
    ]);

    if (!protestData) {
      throw new Error('Protest not found');
    }

    const penaltyUiDefaults: Record<string, { label: string; description: string; defaultValue: number }> = {
      time_penalty: { label: 'Time Penalty', description: 'Add seconds to race result', defaultValue: 5 },
      grid_penalty: { label: 'Grid Penalty', description: 'Grid positions for next race', defaultValue: 3 },
      points_deduction: { label: 'Points Deduction', description: 'Deduct championship points', defaultValue: 5 },
      disqualification: { label: 'Disqualification', description: 'Disqualify from race', defaultValue: 0 },
      warning: { label: 'Warning', description: 'Official warning only', defaultValue: 0 },
      license_points: { label: 'License Points', description: 'Safety rating penalty', defaultValue: 2 },
    };

    const penaltyTypes = (penaltyTypesReference?.penaltyTypes ?? []).map((ref: any) => {
      const ui = penaltyUiDefaults[ref.type];
      const valueLabel = this.getPenaltyValueLabel(String(ref.valueKind ?? 'none'));
      const defaultValue = ui?.defaultValue ?? this.getFallbackDefaultPenaltyValue(String(ref.valueKind ?? 'none'));

      return {
        type: String(ref.type),
        label: ui?.label ?? String(ref.type).replaceAll('_', ' '),
        description: ui?.description ?? '',
        requiresValue: Boolean(ref.requiresValue),
        valueLabel,
        defaultValue,
      };
    });

    const timePenalty = penaltyTypes.find((p) => p.type === 'time_penalty');
    const initial = timePenalty ?? penaltyTypes[0];

    return {
      protest: protestData.protest,
      race: protestData.race,
      protestingDriver: protestData.protestingDriver,
      accusedDriver: protestData.accusedDriver,
      penaltyTypes,
      defaultReasons: penaltyTypesReference?.defaultReasons ?? { upheld: '', dismissed: '' },
      initialPenaltyType: initial?.type ?? null,
      initialPenaltyValue: initial?.defaultValue ?? 0,
    };
  }

  /**
   * Review a protest
   */
  async reviewProtest(input: { protestId: string; stewardId: string; decision: string; decisionNotes: string }): Promise<void> {
    await this.protestService.reviewProtest(input);
  }

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