import type { ImageServicePort } from '@core/racing/application/ports/ImageServicePort';
import type {
    GetRaceResultsDetailErrorCode,
    GetRaceResultsDetailResult,
} from '@core/racing/application/use-cases/GetRaceResultsDetailUseCase';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceResultDTO } from '../dtos/RaceResultDTO';
import type { RaceResultsDetailDTO } from '../dtos/RaceResultsDetailDTO';

export type GetRaceResultsDetailResponseModel = RaceResultsDetailDTO;

export type GetRaceResultsDetailApplicationError = ApplicationErrorCode<
  GetRaceResultsDetailErrorCode,
  { message: string }
>;

export class RaceResultsDetailPresenter {
  private result: GetRaceResultsDetailResult | null = null;

  constructor(private readonly imageService: ImageServicePort) {}

  present(result: GetRaceResultsDetailResult): void {
    this.result = result;
  }

  async getResponseModel(): Promise<GetRaceResultsDetailResponseModel | null> {
    if (!this.result) {
      return null;
    }

    const output = this.result;

    const driverMap = new Map(output.drivers.map(driver => [driver.id, driver]));

    const results: RaceResultDTO[] = await Promise.all(
      output.results.map(async singleResult => {
        const driver = driverMap.get(singleResult.driverId.toString());
        if (!driver) {
          throw new Error(`Driver not found for result: ${singleResult.driverId}`);
        }

        const avatarUrl = this.imageService.getDriverAvatar(driver.id);

        return {
          driverId: singleResult.driverId.toString(),
          driverName: driver.name.toString(),
          avatarUrl,
          position: singleResult.position.toNumber(),
          startPosition: singleResult.startPosition.toNumber(),
          incidents: singleResult.incidents.toNumber(),
          fastestLap: singleResult.fastestLap.toNumber(),
          positionChange: singleResult.getPositionChange(),
          isPodium: singleResult.isPodium(),
          isClean: singleResult.isClean(),
        } as RaceResultDTO;
      }),
    );

    return {
      raceId: output.race.id,
      track: output.race.track,
      results,
    } as RaceResultsDetailDTO;
  }

  get viewModel(): Promise<GetRaceResultsDetailResponseModel> {
    return this.getResponseModel().then(model => {
      if (!model) throw new Error('Presenter not presented');
      return model;
    });
  }
}
