import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
import type { GetRaceDetailResult } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
import type { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
import type { GetRaceDetailParamsDTO } from '../dtos/GetRaceDetailParamsDTO';
import type { RaceDetailDTO } from '../dtos/RaceDetailDTO';
import type { RaceDetailRaceDTO } from '../dtos/RaceDetailRaceDTO';
import type { RaceDetailLeagueDTO } from '../dtos/RaceDetailLeagueDTO';
import type { RaceDetailEntryDTO } from '../dtos/RaceDetailEntryDTO';
import type { RaceDetailRegistrationDTO } from '../dtos/RaceDetailRegistrationDTO';
import type { RaceDetailUserResultDTO } from '../dtos/RaceDetailUserResultDTO';

export type GetRaceDetailResponseModel = RaceDetailDTO;

export class RaceDetailPresenter implements UseCaseOutputPort<GetRaceDetailResult> {
  private result: GetRaceDetailResult | null = null;

  constructor(
    private readonly driverRatingProvider: DriverRatingProvider,
    private readonly imageService: IImageServicePort,
    private readonly params: GetRaceDetailParamsDTO,
  ) {}

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

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

    const output = this.result;
    const params = this.params;

    const raceDTO: RaceDetailRaceDTO | null = output.race
      ? {
          id: output.race.id,
          leagueId: output.race.leagueId,
          track: output.race.track,
          car: output.race.car,
          scheduledAt: output.race.scheduledAt.toISOString(),
          sessionType: output.race.sessionType.toString(),
          status: output.race.status.toString(),
          strengthOfField: output.race.strengthOfField?.toNumber() ?? null,
          ...(output.race.registeredCount !== undefined && {
            registeredCount: output.race.registeredCount.toNumber(),
          }),
          ...(output.race.maxParticipants !== undefined && {
            maxParticipants: output.race.maxParticipants.toNumber(),
          }),
        }
      : null;

    const leagueDTO: RaceDetailLeagueDTO | null = output.league
      ? {
          id: output.league.id.toString(),
          name: output.league.name.toString(),
          description: output.league.description.toString(),
          settings: {
            ...(output.league.settings.maxDrivers !== undefined && { maxDrivers: output.league.settings.maxDrivers }),
            ...(output.league.settings.qualifyingFormat !== undefined && { qualifyingFormat: output.league.settings.qualifyingFormat.toString() }),
          },
        }
      : null;

    const entryListDTO: RaceDetailEntryDTO[] = await Promise.all(
      output.drivers.map(async driver => {
        const rating = this.driverRatingProvider.getRating(driver.id);
        const avatarUrl = this.imageService.getDriverAvatar(driver.id);
        return {
          id: driver.id,
          name: driver.name.toString(),
          country: driver.country.toString(),
          avatarUrl,
          rating,
          isCurrentUser: driver.id === params.driverId,
        };
      }),
    );

    const registrationDTO: RaceDetailRegistrationDTO = {
      isUserRegistered: output.isUserRegistered,
      canRegister: output.canRegister,
    };

    const userResultDTO: RaceDetailUserResultDTO | null = output.userResult
      ? {
          position: output.userResult.position.toNumber(),
          startPosition: output.userResult.startPosition.toNumber(),
          incidents: output.userResult.incidents.toNumber(),
          fastestLap: output.userResult.fastestLap.toNumber(),
          positionChange: output.userResult.getPositionChange(),
          isPodium: output.userResult.isPodium(),
          isClean: output.userResult.isClean(),
          ratingChange: this.calculateRatingChange(output.userResult.position.toNumber()),
        }
      : null;

    return {
      race: raceDTO,
      league: leagueDTO,
      entryList: entryListDTO,
      registration: registrationDTO,
      userResult: userResultDTO,
    } as RaceDetailDTO;
  }

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

  private calculateRatingChange(position: number): number {
    const baseChange = position <= 3 ? 25 : position <= 10 ? 10 : -5;
    const positionBonus = Math.max(0, (20 - position) * 2);
    return baseChange + positionBonus;
  }
}
