import { Inject, Injectable } from '@nestjs/common';
import { Result } from '@core/shared/application/Result';
import { CompleteOnboardingInputDTO } from './dtos/CompleteOnboardingInputDTO';
import { CompleteOnboardingOutputDTO } from './dtos/CompleteOnboardingOutputDTO';
import { DriverRegistrationStatusDTO } from './dtos/DriverRegistrationStatusDTO';
import { DriversLeaderboardDTO } from './dtos/DriversLeaderboardDTO';
import { DriverStatsDTO } from './dtos/DriverStatsDTO';
import { GetDriverOutputDTO } from './dtos/GetDriverOutputDTO';
import { GetDriverProfileOutputDTO } from './dtos/GetDriverProfileOutputDTO';
import { GetDriverRegistrationStatusQueryDTO } from './dtos/GetDriverRegistrationStatusQueryDTO';

// Use cases
import { CompleteDriverOnboardingUseCase } from '@core/racing/application/use-cases/CompleteDriverOnboardingUseCase';
import { GetDriversLeaderboardUseCase } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase';
import { GetProfileOverviewUseCase } from '@core/racing/application/use-cases/GetProfileOverviewUseCase';
import { GetTotalDriversUseCase } from '@core/racing/application/use-cases/GetTotalDriversUseCase';
import { IsDriverRegisteredForRaceUseCase } from '@core/racing/application/use-cases/IsDriverRegisteredForRaceUseCase';
import { UpdateDriverProfileUseCase, type UpdateDriverProfileInput } from '@core/racing/application/use-cases/UpdateDriverProfileUseCase';

// Presenters
import { CompleteOnboardingPresenter } from './presenters/CompleteOnboardingPresenter';
import { DriverPresenter } from './presenters/DriverPresenter';
import { DriverProfilePresenter } from './presenters/DriverProfilePresenter';
import { DriverRegistrationStatusPresenter } from './presenters/DriverRegistrationStatusPresenter';
import { DriversLeaderboardPresenter } from './presenters/DriversLeaderboardPresenter';
import { DriverStatsPresenter } from './presenters/DriverStatsPresenter';

// Tokens
import type { IDriverRepository } from '@core/racing/domain/repositories/IDriverRepository';
import type { Logger } from '@core/shared/application';
import {
  COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN,
  DRIVER_REPOSITORY_TOKEN,
  GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN,
  GET_PROFILE_OVERVIEW_USE_CASE_TOKEN,
  GET_TOTAL_DRIVERS_USE_CASE_TOKEN,
  IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN,
  LOGGER_TOKEN,
  UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN,
} from './DriverTokens';
@Injectable()
export class DriverService {
  constructor(
    @Inject(GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN)
    private readonly getDriversLeaderboardUseCase: GetDriversLeaderboardUseCase,
    @Inject(GET_TOTAL_DRIVERS_USE_CASE_TOKEN)
    private readonly getTotalDriversUseCase: GetTotalDriversUseCase,
    @Inject(COMPLETE_DRIVER_ONBOARDING_USE_CASE_TOKEN)
    private readonly completeDriverOnboardingUseCase: CompleteDriverOnboardingUseCase,
    @Inject(IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN)
    private readonly isDriverRegisteredForRaceUseCase: IsDriverRegisteredForRaceUseCase,
    @Inject(UPDATE_DRIVER_PROFILE_USE_CASE_TOKEN)
    private readonly updateDriverProfileUseCase: UpdateDriverProfileUseCase,
    @Inject(GET_PROFILE_OVERVIEW_USE_CASE_TOKEN)
    private readonly getProfileOverviewUseCase: GetProfileOverviewUseCase,
    @Inject(DRIVER_REPOSITORY_TOKEN)
    private readonly driverRepository: IDriverRepository, // TODO must be removed from service
    @Inject(LOGGER_TOKEN)
    private readonly logger: Logger,
    // Injected presenters (optional for module test compatibility)
    private readonly driversLeaderboardPresenter?: DriversLeaderboardPresenter,
    private readonly driverStatsPresenter?: DriverStatsPresenter,
    private readonly completeOnboardingPresenter?: CompleteOnboardingPresenter,
    private readonly driverRegistrationStatusPresenter?: DriverRegistrationStatusPresenter,
    private readonly driverPresenter?: DriverPresenter,
    private readonly driverProfilePresenter?: DriverProfilePresenter,
  ) {
    // Presenters are configured by providers, no need to configure here
  }

  async getDriversLeaderboard(): Promise<DriversLeaderboardDTO> {
    this.logger.debug('[DriverService] Fetching drivers leaderboard.');

    const result = await this.getDriversLeaderboardUseCase.execute({});
    if (result.isErr()) {
      throw new Error(result.unwrapErr().details.message);
    }
    return this.driversLeaderboardPresenter!.getResponseModel();
  }

  async getTotalDrivers(): Promise<DriverStatsDTO> {
    this.logger.debug('[DriverService] Fetching total drivers count.');

    const result = await this.getTotalDriversUseCase.execute({});
    if (result.isErr()) {
      throw new Error(result.unwrapErr().details.message);
    }
    return this.driverStatsPresenter!.getResponseModel();
  }

  async completeOnboarding(
    userId: string,
    input: CompleteOnboardingInputDTO,
  ): Promise<CompleteOnboardingOutputDTO> {
    this.logger.debug('Completing onboarding for user:', userId);

    const result = await this.completeDriverOnboardingUseCase.execute({
      userId,
      firstName: input.firstName,
      lastName: input.lastName,
      displayName: input.displayName,
      country: input.country,
      ...(input.bio !== undefined ? { bio: input.bio } : {}),
    });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().details.message);
    }
    return this.completeOnboardingPresenter!.getResponseModel();
  }

  async getDriverRegistrationStatus(
    query: GetDriverRegistrationStatusQueryDTO,
  ): Promise<DriverRegistrationStatusDTO> {
    this.logger.debug('Checking driver registration status:', query);

    const result = await this.isDriverRegisteredForRaceUseCase.execute({
      raceId: query.raceId,
      driverId: query.driverId,
    });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().details.message);
    }
    return this.driverRegistrationStatusPresenter!.getResponseModel();
  }

  async getCurrentDriver(userId: string): Promise<GetDriverOutputDTO | null> {
    this.logger.debug(`[DriverService] Fetching current driver for userId: ${userId}`);

    const driver = await this.driverRepository.findById(userId);
    await this.driverPresenter!.present(Result.ok(driver));
    return this.driverPresenter!.getResponseModel();
  }

  async updateDriverProfile(
    driverId: string,
    bio?: string,
    country?: string,
  ): Promise<GetDriverOutputDTO | null> {
    this.logger.debug(`[DriverService] Updating driver profile for driverId: ${driverId}`);

    const input: UpdateDriverProfileInput = { driverId };
    if (bio !== undefined) input.bio = bio;
    if (country !== undefined) input.country = country;

    await this.updateDriverProfileUseCase.execute(input);
    
    // Get the updated driver and present it
    const driver = await this.driverRepository.findById(driverId);
    await this.driverPresenter!.present(Result.ok(driver));
    return this.driverPresenter!.getResponseModel();
  }

  async getDriver(driverId: string): Promise<GetDriverOutputDTO | null> {
    this.logger.debug(`[DriverService] Fetching driver for driverId: ${driverId}`);

    const driver = await this.driverRepository.findById(driverId);
    await this.driverPresenter!.present(Result.ok(driver));
    return this.driverPresenter!.getResponseModel();
  }

  async getDriverProfile(driverId: string): Promise<GetDriverProfileOutputDTO> {
    this.logger.debug(`[DriverService] Fetching driver profile for driverId: ${driverId}`);

    const result = await this.getProfileOverviewUseCase.execute({ driverId });
    if (result.isErr()) {
      throw new Error(result.unwrapErr().details.message);
    }
    return this.driverProfilePresenter!.getResponseModel();
  }
}