import { Injectable, Inject } from '@nestjs/common';
import {
  AllRacesPageViewModel,
  RaceStatsDto,
  ImportRaceResultsInput,
  ImportRaceResultsSummaryViewModel,
  RaceDetailViewModelDto,
  RacesPageDataViewModelDto,
  RaceResultsDetailViewModelDto,
  RaceWithSOFViewModelDto,
  RaceProtestsViewModelDto,
  RacePenaltiesViewModelDto,
  GetRaceDetailParamsDto,
  RegisterForRaceParamsDto,
  WithdrawFromRaceParamsDto,
  RaceActionParamsDto,
} from './dtos/RaceDTO';

// Core imports
import type { Logger } from '@core/shared/application/Logger';

// Use cases
import { GetAllRacesUseCase } from '@core/racing/application/use-cases/GetAllRacesUseCase';
import { GetTotalRacesUseCase } from '@core/racing/application/use-cases/GetTotalRacesUseCase';
import { ImportRaceResultsApiUseCase } from '@core/racing/application/use-cases/ImportRaceResultsApiUseCase';
import { GetRaceDetailUseCase } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
import { GetRacesPageDataUseCase } from '@core/racing/application/use-cases/GetRacesPageDataUseCase';
import { GetAllRacesPageDataUseCase } from '@core/racing/application/use-cases/GetAllRacesPageDataUseCase';
import { GetRaceResultsDetailUseCase } from '@core/racing/application/use-cases/GetRaceResultsDetailUseCase';
import { GetRaceWithSOFUseCase } from '@core/racing/application/use-cases/GetRaceWithSOFUseCase';
import { GetRaceProtestsUseCase } from '@core/racing/application/use-cases/GetRaceProtestsUseCase';
import { GetRacePenaltiesUseCase } from '@core/racing/application/use-cases/GetRacePenaltiesUseCase';
import { RegisterForRaceUseCase } from '@core/racing/application/use-cases/RegisterForRaceUseCase';
import { WithdrawFromRaceUseCase } from '@core/racing/application/use-cases/WithdrawFromRaceUseCase';
import { CancelRaceUseCase } from '@core/racing/application/use-cases/CancelRaceUseCase';
import { CompleteRaceUseCase } from '@core/racing/application/use-cases/CompleteRaceUseCase';
import { ImportRaceResultsUseCase } from '@core/racing/application/use-cases/ImportRaceResultsUseCase';
import { DashboardOverviewUseCase } from '@core/racing/application/use-cases/DashboardOverviewUseCase';
import { FileProtestUseCase } from '@core/racing/application/use-cases/FileProtestUseCase';
import { QuickPenaltyUseCase } from '@core/racing/application/use-cases/QuickPenaltyUseCase';
import { ApplyPenaltyUseCase } from '@core/racing/application/use-cases/ApplyPenaltyUseCase';
import { RequestProtestDefenseUseCase } from '@core/racing/application/use-cases/RequestProtestDefenseUseCase';

// Presenters
import { GetAllRacesPresenter } from './presenters/GetAllRacesPresenter';
import { GetTotalRacesPresenter } from './presenters/GetTotalRacesPresenter';
import { ImportRaceResultsApiPresenter } from './presenters/ImportRaceResultsApiPresenter';

// Tokens
import { LOGGER_TOKEN } from './RaceProviders';

@Injectable()
export class RaceService {
  constructor(
    private readonly getAllRacesUseCase: GetAllRacesUseCase,
    private readonly getTotalRacesUseCase: GetTotalRacesUseCase,
    private readonly importRaceResultsApiUseCase: ImportRaceResultsApiUseCase,
    private readonly getRaceDetailUseCase: GetRaceDetailUseCase,
    private readonly getRacesPageDataUseCase: GetRacesPageDataUseCase,
    private readonly getAllRacesPageDataUseCase: GetAllRacesPageDataUseCase,
    private readonly getRaceResultsDetailUseCase: GetRaceResultsDetailUseCase,
    private readonly getRaceWithSOFUseCase: GetRaceWithSOFUseCase,
    private readonly getRaceProtestsUseCase: GetRaceProtestsUseCase,
    private readonly getRacePenaltiesUseCase: GetRacePenaltiesUseCase,
    private readonly registerForRaceUseCase: RegisterForRaceUseCase,
    private readonly withdrawFromRaceUseCase: WithdrawFromRaceUseCase,
    private readonly cancelRaceUseCase: CancelRaceUseCase,
    private readonly completeRaceUseCase: CompleteRaceUseCase,
    private readonly importRaceResultsUseCase: ImportRaceResultsUseCase,
    private readonly dashboardOverviewUseCase: DashboardOverviewUseCase,
    private readonly fileProtestUseCase: FileProtestUseCase,
    private readonly quickPenaltyUseCase: QuickPenaltyUseCase,
    private readonly applyPenaltyUseCase: ApplyPenaltyUseCase,
    private readonly requestProtestDefenseUseCase: RequestProtestDefenseUseCase,
    @Inject(LOGGER_TOKEN) private readonly logger: Logger,
  ) {}

  async getAllRaces(): Promise<AllRacesPageViewModel> {
    this.logger.debug('[RaceService] Fetching all races.');

    const presenter = new GetAllRacesPresenter();
    await this.getAllRacesUseCase.execute({}, presenter);
    return presenter.getViewModel()!;
  }

  async getTotalRaces(): Promise<RaceStatsDto> {
    this.logger.debug('[RaceService] Fetching total races count.');
    const presenter = new GetTotalRacesPresenter();
    await this.getTotalRacesUseCase.execute({}, presenter);
    return presenter.getViewModel()!;
  }

  async importRaceResults(input: ImportRaceResultsInput): Promise<ImportRaceResultsSummaryViewModel> {
    this.logger.debug('Importing race results:', input);
    const presenter = new ImportRaceResultsApiPresenter();
    await this.importRaceResultsApiUseCase.execute({ raceId: input.raceId, resultsFileContent: input.resultsFileContent }, presenter);
    return presenter.getViewModel()!;
  }

  async getRaceDetail(params: GetRaceDetailParamsDto): Promise<RaceDetailViewModelDto> {
    this.logger.debug('[RaceService] Fetching race detail:', params);

    const presenter = new RaceDetailPresenter();
    const result = await this.getRaceDetailUseCase.execute(params);

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to get race detail');
    }

    return result.value;
  }

  async getRacesPageData(): Promise<RacesPageDataViewModelDto> {
    this.logger.debug('[RaceService] Fetching races page data.');

    const result = await this.getRacesPageDataUseCase.execute();

    if (result.isErr()) {
      throw new Error('Failed to get races page data');
    }

    return result.value;
  }

  async getAllRacesPageData(): Promise<RacesPageDataViewModelDto> {
    this.logger.debug('[RaceService] Fetching all races page data.');

    const result = await this.getAllRacesPageDataUseCase.execute();

    if (result.isErr()) {
      throw new Error('Failed to get all races page data');
    }

    return result.value;
  }

  async getRaceResultsDetail(raceId: string): Promise<RaceResultsDetailViewModelDto> {
    this.logger.debug('[RaceService] Fetching race results detail:', { raceId });

    const result = await this.getRaceResultsDetailUseCase.execute({ raceId });

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to get race results detail');
    }

    return result.value;
  }

  async getRaceWithSOF(raceId: string): Promise<RaceWithSOFViewModelDto> {
    this.logger.debug('[RaceService] Fetching race with SOF:', { raceId });

    const result = await this.getRaceWithSOFUseCase.execute({ raceId });

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to get race with SOF');
    }

    return result.value;
  }

  async getRaceProtests(raceId: string): Promise<RaceProtestsViewModelDto> {
    this.logger.debug('[RaceService] Fetching race protests:', { raceId });

    const result = await this.getRaceProtestsUseCase.execute({ raceId });

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to get race protests');
    }

    return result.value;
  }

  async getRacePenalties(raceId: string): Promise<RacePenaltiesViewModelDto> {
    this.logger.debug('[RaceService] Fetching race penalties:', { raceId });

    const result = await this.getRacePenaltiesUseCase.execute({ raceId });

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to get race penalties');
    }

    return result.value;
  }

  async registerForRace(params: RegisterForRaceParamsDto): Promise<void> {
    this.logger.debug('[RaceService] Registering for race:', params);

    const result = await this.registerForRaceUseCase.execute(params);

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to register for race');
    }
  }

  async withdrawFromRace(params: WithdrawFromRaceParamsDto): Promise<void> {
    this.logger.debug('[RaceService] Withdrawing from race:', params);

    const result = await this.withdrawFromRaceUseCase.execute(params);

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to withdraw from race');
    }
  }

  async cancelRace(params: RaceActionParamsDto): Promise<void> {
    this.logger.debug('[RaceService] Cancelling race:', params);

    const result = await this.cancelRaceUseCase.execute({ raceId: params.raceId });

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to cancel race');
    }
  }

  async completeRace(params: RaceActionParamsDto): Promise<void> {
    this.logger.debug('[RaceService] Completing race:', params);

    const result = await this.completeRaceUseCase.execute({ raceId: params.raceId });

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to complete race');
    }
  }

  async importRaceResultsAlt(params: { raceId: string; resultsFileContent: string }): Promise<void> {
    this.logger.debug('[RaceService] Importing race results (alt):', params);

    const result = await this.importRaceResultsUseCase.execute({
      raceId: params.raceId,
      resultsFileContent: params.resultsFileContent,
    });

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to import race results');
    }
  }

  async getDashboardOverview(driverId: string): Promise<any> {
    this.logger.debug('[RaceService] Getting dashboard overview:', { driverId });

    const result = await this.dashboardOverviewUseCase.execute({ driverId });

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to get dashboard overview');
    }

    return result.value;
  }

  async fileProtest(command: any): Promise<any> {
    this.logger.debug('[RaceService] Filing protest:', command);

    const result = await this.fileProtestUseCase.execute(command);

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to file protest');
    }

    return result.value;
  }

  async applyQuickPenalty(command: any): Promise<any> {
    this.logger.debug('[RaceService] Applying quick penalty:', command);

    const result = await this.quickPenaltyUseCase.execute(command);

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to apply quick penalty');
    }

    return result.value;
  }

  async applyPenalty(command: any): Promise<any> {
    this.logger.debug('[RaceService] Applying penalty:', command);

    const result = await this.applyPenaltyUseCase.execute(command);

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to apply penalty');
    }

    return result.value;
  }

  async requestProtestDefense(command: any): Promise<any> {
    this.logger.debug('[RaceService] Requesting protest defense:', command);

    const result = await this.requestProtestDefenseUseCase.execute(command);

    if (result.isErr()) {
      throw new Error(result.error.details.message || 'Failed to request protest defense');
    }

    return result.value;
  }
}
