import type { Race } from '@core/racing/domain/entities/Race';
import type { IRaceRepository } from '@core/racing/application/ports/IRaceRepository';
import type { IIsDriverRegisteredForRaceQuery } from '@core/racing/application/queries/IIsDriverRegisteredForRaceQuery';
import type { IRegisterForRaceUseCase } from '@core/racing/application/use-cases/IRegisterForRaceUseCase';
import type { IWithdrawFromRaceUseCase } from '@core/racing/application/use-cases/IWithdrawFromRaceUseCase';

export interface LeagueScheduleRaceItemViewModel {
  id: string;
  leagueId: string;
  track: string;
  car: string;
  sessionType: string;
  scheduledAt: Date;
  status: Race['status'];
  isUpcoming: boolean;
  isPast: boolean;
  isRegistered: boolean;
}

export interface LeagueScheduleViewModel {
  races: LeagueScheduleRaceItemViewModel[];
}

export interface ILeagueSchedulePresenter {
  loadLeagueSchedule(leagueId: string, driverId: string): Promise<LeagueScheduleViewModel>;
  registerForRace(raceId: string, leagueId: string, driverId: string): Promise<void>;
  withdrawFromRace(raceId: string, driverId: string): Promise<void>;
}

export class LeagueSchedulePresenter implements ILeagueSchedulePresenter {
  constructor(
    private raceRepository: IRaceRepository,
    private isDriverRegisteredForRaceQuery: IIsDriverRegisteredForRaceQuery,
    private registerForRaceUseCase: IRegisterForRaceUseCase,
    private withdrawFromRaceUseCase: IWithdrawFromRaceUseCase,
  ) {}

  /**
   * Load league schedule with registration status for a given driver.
   */
  async loadLeagueSchedule(
    leagueId: string,
    driverId: string,
  ): Promise<LeagueScheduleViewModel> {
    const allRaces = await this.raceRepository.findAll();
    const leagueRaces = allRaces
      .filter((race) => race.leagueId === leagueId)
      .sort(
        (a, b) =>
          new Date(a.scheduledAt).getTime() - new Date(b.scheduledAt).getTime(),
      );

    const now = new Date();

    const registrationStates: Record<string, boolean> = {};
    await Promise.all(
      leagueRaces.map(async (race) => {
        const registered = await this.isDriverRegisteredForRaceQuery.execute({
          raceId: race.id,
          driverId,
        });
        registrationStates[race.id] = registered;
      }),
    );

    const races: LeagueScheduleRaceItemViewModel[] = leagueRaces.map((race) => {
      const raceDate = new Date(race.scheduledAt);
      const isPast = race.status === 'completed' || raceDate <= now;
      const isUpcoming = race.status === 'scheduled' && raceDate > now;

      return {
        id: race.id,
        leagueId: race.leagueId,
        track: race.track,
        car: race.car,
        sessionType: race.sessionType,
        scheduledAt: raceDate,
        status: race.status,
        isUpcoming,
        isPast,
        isRegistered: registrationStates[race.id] ?? false,
      };
    });

    return { races };
  }

  /**
   * Register the driver for a race.
   */
  async registerForRace(
    raceId: string,
    leagueId: string,
    driverId: string,
  ): Promise<void> {
    await this.registerForRaceUseCase.execute({
      raceId,
      leagueId,
      driverId,
    });
  }

  /**
   * Withdraw the driver from a race.
   */
  async withdrawFromRace(
    raceId: string,
    driverId: string,
  ): Promise<void> {
    await this.withdrawFromRaceUseCase.execute({
      raceId,
      driverId,
    });
  }
}