import type { Provider } from '@nestjs/common';

// Import core interfaces
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
import type { DriverRepository } from '@core/racing/domain/repositories/DriverRepository';
import type { LeagueMembershipRepository } from '@core/racing/domain/repositories/LeagueMembershipRepository';
import type { LeagueRepository } from '@core/racing/domain/repositories/LeagueRepository';
import type { PenaltyRepository } from '@core/racing/domain/repositories/PenaltyRepository';
import type { ProtestRepository } from '@core/racing/domain/repositories/ProtestRepository';
import type { RaceRegistrationRepository } from '@core/racing/domain/repositories/RaceRegistrationRepository';
import type { RaceRepository } from '@core/racing/domain/repositories/RaceRepository';
import type { ResultRepository } from '@core/racing/domain/repositories/ResultRepository';
import type { StandingRepository } from '@core/racing/domain/repositories/StandingRepository';
import type { Logger } from '@core/shared/domain/Logger';

// Import concrete in-memory implementations
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
import { InMemoryDriverRatingProvider } from '@adapters/racing/ports/InMemoryDriverRatingProvider';

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

// Import presenters
import { AllRacesPageDataPresenter } from './presenters/AllRacesPageDataPresenter';
import { CommandResultPresenter } from './presenters/CommandResultPresenter';
import { GetAllRacesPresenter } from './presenters/GetAllRacesPresenter';
import { GetTotalRacesPresenter } from './presenters/GetTotalRacesPresenter';
import { ImportRaceResultsApiPresenter } from './presenters/ImportRaceResultsApiPresenter';
import { RaceDetailPresenter } from './presenters/RaceDetailPresenter';
import { RacePenaltiesPresenter } from './presenters/RacePenaltiesPresenter';
import { RaceProtestsPresenter } from './presenters/RaceProtestsPresenter';
import { RaceResultsDetailPresenter } from './presenters/RaceResultsDetailPresenter';
import { RacesPageDataPresenter } from './presenters/RacesPageDataPresenter';
import { RaceWithSOFPresenter } from './presenters/RaceWithSOFPresenter';

import {
    ALL_RACES_PAGE_DATA_PRESENTER_TOKEN,
    COMMAND_RESULT_PRESENTER_TOKEN,
    DRIVER_RATING_PROVIDER_TOKEN,
    DRIVER_REPOSITORY_TOKEN,
    GET_ALL_RACES_PRESENTER_TOKEN,
    GET_TOTAL_RACES_PRESENTER_TOKEN,
    IMAGE_SERVICE_TOKEN,
    IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN,
    LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
    LEAGUE_REPOSITORY_TOKEN,
    LOGGER_TOKEN,
    PENALTY_REPOSITORY_TOKEN,
    PROTEST_REPOSITORY_TOKEN,
    RACE_DETAIL_PRESENTER_TOKEN,
    RACE_PENALTIES_PRESENTER_TOKEN,
    RACE_PROTESTS_PRESENTER_TOKEN,
    RACE_REGISTRATION_REPOSITORY_TOKEN,
    RACE_REPOSITORY_TOKEN,
    RACE_RESULTS_DETAIL_PRESENTER_TOKEN,
    RACE_WITH_SOF_PRESENTER_TOKEN,
    RACES_PAGE_DATA_PRESENTER_TOKEN,
    RESULT_REPOSITORY_TOKEN,
    STANDING_REPOSITORY_TOKEN,
} from './RaceTokens';

export * from './RaceTokens';

export const RaceProviders: Provider[] = [
  {
    provide: DRIVER_RATING_PROVIDER_TOKEN,
    useFactory: (logger: Logger) => new InMemoryDriverRatingProvider(logger),
    inject: [LOGGER_TOKEN],
  },
  {
    provide: IMAGE_SERVICE_TOKEN,
    useFactory: (logger: Logger) => new InMemoryImageServiceAdapter(logger),
    inject: [LOGGER_TOKEN],
  },
  {
    provide: LOGGER_TOKEN,
    useClass: ConsoleLogger,
  },
  // Presenters
  {
    provide: GET_ALL_RACES_PRESENTER_TOKEN,
    useClass: GetAllRacesPresenter,
  },
  {
    provide: GET_TOTAL_RACES_PRESENTER_TOKEN,
    useClass: GetTotalRacesPresenter,
  },
  {
    provide: IMPORT_RACE_RESULTS_API_PRESENTER_TOKEN,
    useClass: ImportRaceResultsApiPresenter,
  },
  {
    provide: RACE_DETAIL_PRESENTER_TOKEN,
    useFactory: (driverRatingProvider: DriverRatingProvider, imageService: InMemoryImageServiceAdapter) => 
      new RaceDetailPresenter(driverRatingProvider, imageService),
    inject: [DRIVER_RATING_PROVIDER_TOKEN, IMAGE_SERVICE_TOKEN],
  },
  {
    provide: RACES_PAGE_DATA_PRESENTER_TOKEN,
    useClass: RacesPageDataPresenter,
  },
  {
    provide: ALL_RACES_PAGE_DATA_PRESENTER_TOKEN,
    useClass: AllRacesPageDataPresenter,
  },
  {
    provide: RACE_RESULTS_DETAIL_PRESENTER_TOKEN,
    useFactory: (imageService: InMemoryImageServiceAdapter) => new RaceResultsDetailPresenter(imageService),
    inject: [IMAGE_SERVICE_TOKEN],
  },
  {
    provide: RACE_WITH_SOF_PRESENTER_TOKEN,
    useClass: RaceWithSOFPresenter,
  },
  {
    provide: RACE_PROTESTS_PRESENTER_TOKEN,
    useClass: RaceProtestsPresenter,
  },
  {
    provide: RACE_PENALTIES_PRESENTER_TOKEN,
    useClass: RacePenaltiesPresenter,
  },
  {
    provide: COMMAND_RESULT_PRESENTER_TOKEN,
    useClass: CommandResultPresenter,
  },
  // Use cases
  {
    provide: GetAllRacesUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      leagueRepo: LeagueRepository,
      logger: Logger,
    ) => {
      return new GetAllRacesUseCase(raceRepo, leagueRepo, logger);
    },
    inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: GetTotalRacesUseCase,
    useFactory: (
      raceRepo: RaceRepository,
    ) => {
      return new GetTotalRacesUseCase(raceRepo);
    },
    inject: [RACE_REPOSITORY_TOKEN],
  },
  {
    provide: ImportRaceResultsApiUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      leagueRepo: LeagueRepository,
      resultRepo: ResultRepository,
      driverRepo: DriverRepository,
      standingRepo: StandingRepository,
      logger: Logger,
    ) => {
      return new ImportRaceResultsApiUseCase(
        raceRepo,
        leagueRepo,
        resultRepo,
        driverRepo,
        standingRepo,
        logger,
      );
    },
    inject: [
      RACE_REPOSITORY_TOKEN,
      LEAGUE_REPOSITORY_TOKEN,
      RESULT_REPOSITORY_TOKEN,
      DRIVER_REPOSITORY_TOKEN,
      STANDING_REPOSITORY_TOKEN,
      LOGGER_TOKEN,
    ],
  },
  {
    provide: GetRaceDetailUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      leagueRepo: LeagueRepository,
      driverRepo: DriverRepository,
      raceRegRepo: RaceRegistrationRepository,
      resultRepo: ResultRepository,
      leagueMembershipRepo: LeagueMembershipRepository,
    ) => {
      return new GetRaceDetailUseCase(
        raceRepo,
        leagueRepo,
        driverRepo,
        raceRegRepo,
        resultRepo,
        leagueMembershipRepo,
      );
    },
    inject: [
      RACE_REPOSITORY_TOKEN,
      LEAGUE_REPOSITORY_TOKEN,
      DRIVER_REPOSITORY_TOKEN,
      RACE_REGISTRATION_REPOSITORY_TOKEN,
      RESULT_REPOSITORY_TOKEN,
      LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
    ],
  },
  {
    provide: GetRacesPageDataUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      leagueRepo: LeagueRepository,
      logger: Logger,
    ) => {
      return new GetRacesPageDataUseCase(
        raceRepo,
        leagueRepo,
        logger,
      );
    },
    inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: GetAllRacesPageDataUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      leagueRepo: LeagueRepository,
      logger: Logger,
    ) => {
      return new GetAllRacesPageDataUseCase(
        raceRepo,
        leagueRepo,
        logger,
      );
    },
    inject: [RACE_REPOSITORY_TOKEN, LEAGUE_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: GetRaceResultsDetailUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      leagueRepo: LeagueRepository,
      resultRepo: ResultRepository,
      driverRepo: DriverRepository,
      penaltyRepo: PenaltyRepository,
    ) => {
      return new GetRaceResultsDetailUseCase(
        raceRepo,
        leagueRepo,
        resultRepo,
        driverRepo,
        penaltyRepo,
      );
    },
    inject: [
      RACE_REPOSITORY_TOKEN,
      LEAGUE_REPOSITORY_TOKEN,
      RESULT_REPOSITORY_TOKEN,
      DRIVER_REPOSITORY_TOKEN,
      PENALTY_REPOSITORY_TOKEN,
    ],
  },
  {
    provide: GetRaceWithSOFUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      raceRegRepo: RaceRegistrationRepository,
      resultRepo: ResultRepository,
      driverRatingProvider: DriverRatingProvider,
    ) => {
      return new GetRaceWithSOFUseCase(
        raceRepo,
        raceRegRepo,
        resultRepo,
        async (input: { driverId: string }) => {
          const rating = driverRatingProvider.getRating(input.driverId);
          return { rating };
        },
      );
    },
    inject: [
      RACE_REPOSITORY_TOKEN,
      RACE_REGISTRATION_REPOSITORY_TOKEN,
      RESULT_REPOSITORY_TOKEN,
      DRIVER_RATING_PROVIDER_TOKEN,
    ],
  },
  {
    provide: GetRaceProtestsUseCase,
    useFactory: (
      protestRepo: ProtestRepository,
      driverRepo: DriverRepository,
    ) => {
      return new GetRaceProtestsUseCase(
        protestRepo,
        driverRepo,
      );
    },
    inject: [PROTEST_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
  },
  {
    provide: GetRacePenaltiesUseCase,
    useFactory: (
      penaltyRepo: PenaltyRepository,
      driverRepo: DriverRepository,
    ) => {
      return new GetRacePenaltiesUseCase(
        penaltyRepo,
        driverRepo,
      );
    },
    inject: [PENALTY_REPOSITORY_TOKEN, DRIVER_REPOSITORY_TOKEN],
  },
  {
    provide: RegisterForRaceUseCase,
    useFactory: (
      raceRegRepo: RaceRegistrationRepository,
      leagueMembershipRepo: LeagueMembershipRepository,
      logger: Logger,
    ) => {
      return new RegisterForRaceUseCase(
        raceRegRepo,
        leagueMembershipRepo,
        logger,
      );
    },
    inject: [RACE_REGISTRATION_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: WithdrawFromRaceUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      raceRegRepo: RaceRegistrationRepository,
      logger: Logger,
    ) => {
      return new WithdrawFromRaceUseCase(
        raceRepo,
        raceRegRepo,
        logger,
      );
    },
    inject: [RACE_REPOSITORY_TOKEN, RACE_REGISTRATION_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: CancelRaceUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      logger: Logger,
    ) => {
      return new CancelRaceUseCase(
        raceRepo,
        logger,
      );
    },
    inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: CompleteRaceUseCase,
    useFactory: (
      leagueRepo: LeagueRepository,
      raceRepo: RaceRepository,
      raceRegRepo: RaceRegistrationRepository,
      resultRepo: ResultRepository,
      standingRepo: StandingRepository,
      driverRatingProvider: DriverRatingProvider,
    ) => {
      return new CompleteRaceUseCase(
        leagueRepo,
        raceRepo,
        raceRegRepo,
        resultRepo,
        standingRepo,
        async (input: { driverId: string }) => {
          const rating = driverRatingProvider.getRating(input.driverId);
          return { rating, ratingChange: null };
        },
      );
    },
    inject: [
      LEAGUE_REPOSITORY_TOKEN,
      RACE_REPOSITORY_TOKEN,
      RACE_REGISTRATION_REPOSITORY_TOKEN,
      RESULT_REPOSITORY_TOKEN,
      STANDING_REPOSITORY_TOKEN,
      DRIVER_RATING_PROVIDER_TOKEN,
    ],
  },
  {
    provide: ReopenRaceUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      logger: Logger,
    ) => {
      return new ReopenRaceUseCase(
        raceRepo,
        logger,
      );
    },
    inject: [RACE_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: ImportRaceResultsUseCase,
    useFactory: (
      raceRepo: RaceRepository,
      leagueRepo: LeagueRepository,
      resultRepo: ResultRepository,
      driverRepo: DriverRepository,
      standingRepo: StandingRepository,
      logger: Logger,
    ) => {
      return new ImportRaceResultsUseCase(
        raceRepo,
        leagueRepo,
        resultRepo,
        driverRepo,
        standingRepo,
        logger,
      );
    },
    inject: [
      RACE_REPOSITORY_TOKEN,
      LEAGUE_REPOSITORY_TOKEN,
      RESULT_REPOSITORY_TOKEN,
      DRIVER_REPOSITORY_TOKEN,
      STANDING_REPOSITORY_TOKEN,
      LOGGER_TOKEN,
    ],
  },
  {
    provide: FileProtestUseCase,
    useFactory: (
      protestRepo: ProtestRepository,
      raceRepo: RaceRepository,
      leagueMembershipRepo: LeagueMembershipRepository,
    ) => {
      return new FileProtestUseCase(
        protestRepo,
        raceRepo,
        leagueMembershipRepo,
      );
    },
    inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN],
  },
  {
    provide: QuickPenaltyUseCase,
    useFactory: (
      penaltyRepo: PenaltyRepository,
      raceRepo: RaceRepository,
      leagueMembershipRepo: LeagueMembershipRepository,
      logger: Logger,
    ) => {
      return new QuickPenaltyUseCase(
        penaltyRepo,
        raceRepo,
        leagueMembershipRepo,
        logger,
      );
    },
    inject: [PENALTY_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: ApplyPenaltyUseCase,
    useFactory: (
      penaltyRepo: PenaltyRepository,
      protestRepo: ProtestRepository,
      raceRepo: RaceRepository,
      leagueMembershipRepo: LeagueMembershipRepository,
      logger: Logger,
    ) => {
      return new ApplyPenaltyUseCase(
        penaltyRepo,
        protestRepo,
        raceRepo,
        leagueMembershipRepo,
        logger,
      );
    },
    inject: [PENALTY_REPOSITORY_TOKEN, PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: RequestProtestDefenseUseCase,
    useFactory: (
      protestRepo: ProtestRepository,
      raceRepo: RaceRepository,
      leagueMembershipRepo: LeagueMembershipRepository,
      logger: Logger,
    ) => {
      return new RequestProtestDefenseUseCase(
        protestRepo,
        raceRepo,
        leagueMembershipRepo,
        logger,
      );
    },
    inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
  {
    provide: ReviewProtestUseCase,
    useFactory: (
      protestRepo: ProtestRepository,
      raceRepo: RaceRepository,
      leagueMembershipRepo: LeagueMembershipRepository,
      logger: Logger,
    ) => {
      return new ReviewProtestUseCase(
        protestRepo,
        raceRepo,
        leagueMembershipRepo,
        logger,
      );
    },
    inject: [PROTEST_REPOSITORY_TOKEN, RACE_REPOSITORY_TOKEN, LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN, LOGGER_TOKEN],
  },
];