import type { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type {
  GetRaceProtestsResult,
  GetRaceProtestsErrorCode,
} from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
import type { RaceProtestsDTO } from '../dtos/RaceProtestsDTO';
import type { RaceProtestDTO } from '../dtos/RaceProtestDTO';

export type GetRaceProtestsResponseModel = RaceProtestsDTO;

export type GetRaceProtestsApplicationError = ApplicationErrorCode<
  GetRaceProtestsErrorCode,
  { message: string }
>;

export class RaceProtestsPresenter {
  private model: GetRaceProtestsResponseModel | null = null;

  reset(): void {
    this.model = null;
  }

  present(result: Result<GetRaceProtestsResult, GetRaceProtestsApplicationError>): void {
    if (result.isErr()) {
      const error = result.unwrapErr();
      throw new Error(error.details?.message ?? 'Failed to get race protests');
    }

    const output = result.unwrap();

    const protests: RaceProtestDTO[] = output.protests.map(protest => ({
      id: protest.id,
      protestingDriverId: protest.protestingDriverId,
      accusedDriverId: protest.accusedDriverId,
      incident: {
        lap: protest.incident.lap.toNumber(),
        description: protest.incident.description.toString(),
      },
      status: protest.status.toString(),
      filedAt: protest.filedAt.toISOString(),
    } as RaceProtestDTO));

    const driverMap: Record<string, string> = {};
    output.drivers.forEach(driver => {
      driverMap[driver.id] = driver.name.toString();
    });

    this.model = {
      protests,
      driverMap,
    } as RaceProtestsDTO;
  }

  getResponseModel(): GetRaceProtestsResponseModel | null {
    return this.model;
  }

  get responseModel(): GetRaceProtestsResponseModel {
    if (!this.model) {
      throw new Error('Presenter not presented');
    }

    return this.model;
  }

  get viewModel(): GetRaceProtestsResponseModel {
    return this.responseModel;
  }
}
