import type { AsyncUseCase } from '@core/shared/application';
import type { Logger } from '@core/shared/application';
import type { IRaceEventRepository } from '../../domain/repositories/IRaceEventRepository';
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository';
import type { DomainEventPublisher } from '@/shared/domain/DomainEvent';
import { RaceEventStewardingClosedEvent } from '../../domain/events/RaceEventStewardingClosed';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { CloseRaceEventStewardingCommand } from '../dto/CloseRaceEventStewardingCommand';
import type { RaceEvent } from '../../domain/entities/RaceEvent';

/**
 * Use Case: CloseRaceEventStewardingUseCase
 *
 * Scheduled job that checks for race events with expired stewarding windows
 * and closes them, triggering final results notifications.
 *
 * This would typically be run by a scheduled job (e.g., every 5 minutes)
 * to automatically close stewarding windows based on league configuration.
 */
export class CloseRaceEventStewardingUseCase
  implements AsyncUseCase<CloseRaceEventStewardingCommand, void, string>
{
  constructor(
    private readonly logger: Logger,

    private readonly raceEventRepository: IRaceEventRepository,
    private readonly raceRegistrationRepository: IRaceRegistrationRepository,
    private readonly penaltyRepository: IPenaltyRepository,
    private readonly domainEventPublisher: DomainEventPublisher,
  ) {}

  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  async execute(_command: CloseRaceEventStewardingCommand): Promise<Result<void, ApplicationErrorCode<string>>> {
    try {
      // Find all race events awaiting stewarding that have expired windows
      const expiredEvents = await this.raceEventRepository.findAwaitingStewardingClose();

      for (const raceEvent of expiredEvents) {
        await this.closeStewardingForRaceEvent(raceEvent);
      }

      return Result.ok(undefined);
    } catch (error) {
      this.logger.error('Failed to close race event stewarding', error instanceof Error ? error : new Error(String(error)));
      return Result.err({ code: 'FAILED_TO_CLOSE_STEWARDING' });
    }
  }

  private async closeStewardingForRaceEvent(raceEvent: RaceEvent): Promise<void> {
    try {
      // Close the stewarding window
      const closedRaceEvent = raceEvent.closeStewarding();
      await this.raceEventRepository.update(closedRaceEvent);

      // Get list of participating drivers
      const driverIds = await this.getParticipatingDriverIds(raceEvent);

      // Check if any penalties were applied during stewarding
      const hadPenaltiesApplied = await this.checkForAppliedPenalties(raceEvent);

      // Publish domain event to trigger final results notifications
      const event = new RaceEventStewardingClosedEvent({
        raceEventId: raceEvent.id,
        leagueId: raceEvent.leagueId,
        seasonId: raceEvent.seasonId,
        closedAt: new Date(),
        driverIds,
        hadPenaltiesApplied,
      });

      await this.domainEventPublisher.publish(event);

    } catch (error) {
      this.logger.error(`Failed to close stewarding for race event ${raceEvent.id}`, error instanceof Error ? error : new Error(String(error)));
      // In production, this would trigger alerts/monitoring
      throw error;
    }
  }

  private async getParticipatingDriverIds(raceEvent: RaceEvent): Promise<string[]> {
    return await this.raceRegistrationRepository.getRegisteredDrivers(raceEvent.id);
  }

  private async checkForAppliedPenalties(raceEvent: RaceEvent): Promise<boolean> {
    const penalties = await this.penaltyRepository.findByRaceId(raceEvent.id);
    return penalties.length > 0;
  }
}