import { BadRequestException, ConflictException, Inject, Injectable, NotFoundException } from '@nestjs/common';
import { ApproveJoinRequestInputDTO } from './dtos/ApproveJoinRequestInputDTO';
import { ApproveLeagueJoinRequestDTO } from './dtos/ApproveLeagueJoinRequestDTO';
import { CreateLeagueInputDTO } from './dtos/CreateLeagueInputDTO';
import { GetLeagueAdminConfigQueryDTO } from './dtos/GetLeagueAdminConfigQueryDTO';
import { GetLeagueAdminPermissionsInputDTO } from './dtos/GetLeagueAdminPermissionsInputDTO';
import { GetLeagueOwnerSummaryQueryDTO } from './dtos/GetLeagueOwnerSummaryQueryDTO';
import { GetLeagueProtestsQueryDTO } from './dtos/GetLeagueProtestsQueryDTO';
import { GetLeagueRacesOutputDTO } from './dtos/GetLeagueRacesOutputDTO';
import { GetLeagueScheduleQueryDTO } from './dtos/GetLeagueScheduleQueryDTO';
import { GetLeagueSeasonsQueryDTO } from './dtos/GetLeagueSeasonsQueryDTO';
import { GetLeagueWalletOutputDTO } from './dtos/GetLeagueWalletOutputDTO';
import { GetSeasonSponsorshipsOutputDTO } from './dtos/GetSeasonSponsorshipsOutputDTO';
import { LeagueAdminDTO } from './dtos/LeagueAdminDTO';
import { LeagueAdminPermissionsDTO } from './dtos/LeagueAdminPermissionsDTO';
import { LeagueAdminProtestsDTO } from './dtos/LeagueAdminProtestsDTO';
import { LeagueConfigFormModelDTO } from './dtos/LeagueConfigFormModelDTO';
import { LeagueJoinRequestWithDriverDTO } from './dtos/LeagueJoinRequestWithDriverDTO';
import { LeagueMembershipsDTO } from './dtos/LeagueMembershipsDTO';
import { LeagueOwnerSummaryDTO } from './dtos/LeagueOwnerSummaryDTO';
import { LeagueRosterJoinRequestDTO } from './dtos/LeagueRosterJoinRequestDTO';
import { LeagueRosterMemberDTO } from './dtos/LeagueRosterMemberDTO';
import { LeagueScheduleDTO } from './dtos/LeagueScheduleDTO';
import {
    CreateLeagueScheduleRaceInputDTO,
    CreateLeagueScheduleRaceOutputDTO,
    LeagueScheduleRaceMutationSuccessDTO,
    UpdateLeagueScheduleRaceInputDTO,
} from './dtos/LeagueScheduleRaceAdminDTO';
import {
    LeagueSeasonSchedulePublishInputDTO,
    LeagueSeasonSchedulePublishOutputDTO,
} from './dtos/LeagueSeasonSchedulePublishDTO';
import { LeagueSeasonSummaryDTO } from './dtos/LeagueSeasonSummaryDTO';
import { LeagueStandingsDTO } from './dtos/LeagueStandingsDTO';
import { LeagueStatsDTO } from './dtos/LeagueStatsDTO';
import { RejectJoinRequestInputDTO } from './dtos/RejectJoinRequestInputDTO';
import { RejectJoinRequestOutputDTO } from './dtos/RejectJoinRequestOutputDTO';
import { RemoveLeagueMemberInputDTO } from './dtos/RemoveLeagueMemberInputDTO';
import { RemoveLeagueMemberOutputDTO } from './dtos/RemoveLeagueMemberOutputDTO';
import { TransferLeagueOwnershipInputDTO } from './dtos/TransferLeagueOwnershipInputDTO';
import { TransferLeagueOwnershipOutputDTO } from './dtos/TransferLeagueOwnershipOutputDTO';
import { UpdateLeagueMemberRoleInputDTO } from './dtos/UpdateLeagueMemberRoleInputDTO';
import { UpdateLeagueMemberRoleOutputDTO } from './dtos/UpdateLeagueMemberRoleOutputDTO';
import { WithdrawFromLeagueWalletInputDTO } from './dtos/WithdrawFromLeagueWalletInputDTO';
import { WithdrawFromLeagueWalletOutputDTO } from './dtos/WithdrawFromLeagueWalletOutputDTO';

import { getActorFromRequestContext } from '../auth/getActorFromRequestContext';
import { requireLeagueAdminOrOwner } from './LeagueAuthorization';

// Core imports for view models
import type { AllLeaguesWithCapacityAndScoringDTO as AllLeaguesWithCapacityAndScoringViewModel } from './dtos/AllLeaguesWithCapacityAndScoringDTO';
import type { AllLeaguesWithCapacityDTO as AllLeaguesWithCapacityViewModel } from './dtos/AllLeaguesWithCapacityDTO';
import type { CreateLeagueViewModel } from './dtos/CreateLeagueDTO';
import type { JoinLeagueOutputDTO } from './dtos/JoinLeagueOutputDTO';
import { TotalLeaguesDTO } from './dtos/TotalLeaguesDTO';
import type { LeagueScoringConfigViewModel } from './presenters/LeagueScoringConfigPresenter';
import type { LeagueScoringPresetsViewModel } from './presenters/LeagueScoringPresetsPresenter';

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

// Use cases
import { ApproveLeagueJoinRequestUseCase } from '@core/racing/application/use-cases/ApproveLeagueJoinRequestUseCase';
import { CreateLeagueWithSeasonAndScoringUseCase } from '@core/racing/application/use-cases/CreateLeagueWithSeasonAndScoringUseCase';
import { GetAllLeaguesWithCapacityAndScoringUseCase } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityAndScoringUseCase';
import { GetAllLeaguesWithCapacityUseCase } from '@core/racing/application/use-cases/GetAllLeaguesWithCapacityUseCase';
import { GetLeagueAdminPermissionsUseCase } from '@core/racing/application/use-cases/GetLeagueAdminPermissionsUseCase';
import { GetLeagueFullConfigUseCase } from '@core/racing/application/use-cases/GetLeagueFullConfigUseCase';
import { GetLeagueJoinRequestsUseCase } from '@core/racing/application/use-cases/GetLeagueJoinRequestsUseCase';
import { GetLeagueMembershipsUseCase } from '@core/racing/application/use-cases/GetLeagueMembershipsUseCase';
import { GetLeagueOwnerSummaryUseCase } from '@core/racing/application/use-cases/GetLeagueOwnerSummaryUseCase';
import { GetLeagueProtestsUseCase } from '@core/racing/application/use-cases/GetLeagueProtestsUseCase';
import { GetLeagueRosterJoinRequestsUseCase } from '@core/racing/application/use-cases/GetLeagueRosterJoinRequestsUseCase';
import { GetLeagueRosterMembersUseCase } from '@core/racing/application/use-cases/GetLeagueRosterMembersUseCase';
import type { GetLeagueScheduleInput } from '@core/racing/application/use-cases/GetLeagueScheduleUseCase';
import { GetLeagueScheduleUseCase } from '@core/racing/application/use-cases/GetLeagueScheduleUseCase';
import { GetLeagueScoringConfigUseCase } from '@core/racing/application/use-cases/GetLeagueScoringConfigUseCase';
import { GetLeagueSeasonsUseCase } from '@core/racing/application/use-cases/GetLeagueSeasonsUseCase';
import { GetLeagueStandingsUseCase } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
import { GetLeagueStatsUseCase } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
import { GetLeagueWalletUseCase } from '@core/racing/application/use-cases/GetLeagueWalletUseCase';
import { GetSeasonSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSeasonSponsorshipsUseCase';
import { GetTotalLeaguesUseCase } from '@core/racing/application/use-cases/GetTotalLeaguesUseCase';
import { JoinLeagueUseCase } from '@core/racing/application/use-cases/JoinLeagueUseCase';
import { ListLeagueScoringPresetsUseCase } from '@core/racing/application/use-cases/ListLeagueScoringPresetsUseCase';
import { RejectLeagueJoinRequestUseCase } from '@core/racing/application/use-cases/RejectLeagueJoinRequestUseCase';
import { RemoveLeagueMemberUseCase } from '@core/racing/application/use-cases/RemoveLeagueMemberUseCase';
import { TransferLeagueOwnershipUseCase } from '@core/racing/application/use-cases/TransferLeagueOwnershipUseCase';
import { UpdateLeagueMemberRoleUseCase } from '@core/racing/application/use-cases/UpdateLeagueMemberRoleUseCase';
import { WithdrawFromLeagueWalletUseCase } from '@core/racing/application/use-cases/WithdrawFromLeagueWalletUseCase';

import { CreateLeagueSeasonScheduleRaceUseCase } from '@core/racing/application/use-cases/CreateLeagueSeasonScheduleRaceUseCase';
import { DeleteLeagueSeasonScheduleRaceUseCase } from '@core/racing/application/use-cases/DeleteLeagueSeasonScheduleRaceUseCase';
import { PublishLeagueSeasonScheduleUseCase } from '@core/racing/application/use-cases/PublishLeagueSeasonScheduleUseCase';
import { UnpublishLeagueSeasonScheduleUseCase } from '@core/racing/application/use-cases/UnpublishLeagueSeasonScheduleUseCase';
import { UpdateLeagueSeasonScheduleRaceUseCase } from '@core/racing/application/use-cases/UpdateLeagueSeasonScheduleRaceUseCase';

// API Presenters
import { AllLeaguesWithCapacityAndScoringPresenter } from './presenters/AllLeaguesWithCapacityAndScoringPresenter';
import { AllLeaguesWithCapacityPresenter } from './presenters/AllLeaguesWithCapacityPresenter';
import { ApproveLeagueJoinRequestPresenter } from './presenters/ApproveLeagueJoinRequestPresenter';
import { CreateLeaguePresenter } from './presenters/CreateLeaguePresenter';
import { GetLeagueAdminPermissionsPresenter } from './presenters/GetLeagueAdminPermissionsPresenter';
import { GetLeagueMembershipsPresenter } from './presenters/GetLeagueMembershipsPresenter';
import { GetLeagueOwnerSummaryPresenter } from './presenters/GetLeagueOwnerSummaryPresenter';
import { GetLeagueProtestsPresenter } from './presenters/GetLeagueProtestsPresenter';
import { GetLeagueSeasonsPresenter } from './presenters/GetLeagueSeasonsPresenter';
import { GetLeagueWalletPresenter } from './presenters/GetLeagueWalletPresenter';
import { GetSeasonSponsorshipsPresenter } from './presenters/GetSeasonSponsorshipsPresenter';
import { JoinLeaguePresenter } from './presenters/JoinLeaguePresenter';
import { LeagueConfigPresenter } from './presenters/LeagueConfigPresenter';
import { LeagueJoinRequestsPresenter } from './presenters/LeagueJoinRequestsPresenter';
import {
    GetLeagueRosterJoinRequestsPresenter,
    GetLeagueRosterMembersPresenter,
} from './presenters/LeagueRosterAdminReadPresenters';
import { LeagueSchedulePresenter } from './presenters/LeagueSchedulePresenter';
import { LeagueScoringConfigPresenter } from './presenters/LeagueScoringConfigPresenter';
import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter';
import {
    CreateLeagueSeasonScheduleRacePresenter,
    DeleteLeagueSeasonScheduleRacePresenter,
    PublishLeagueSeasonSchedulePresenter,
    UnpublishLeagueSeasonSchedulePresenter,
    UpdateLeagueSeasonScheduleRacePresenter,
} from './presenters/LeagueSeasonScheduleMutationPresenters';
import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
import { LeagueStatsPresenter } from './presenters/LeagueStatsPresenter';
import { RejectLeagueJoinRequestPresenter } from './presenters/RejectLeagueJoinRequestPresenter';
import { RemoveLeagueMemberPresenter } from './presenters/RemoveLeagueMemberPresenter';
import { TotalLeaguesPresenter } from './presenters/TotalLeaguesPresenter';
import { TransferLeagueOwnershipPresenter } from './presenters/TransferLeagueOwnershipPresenter';
import { UpdateLeagueMemberRolePresenter } from './presenters/UpdateLeagueMemberRolePresenter';
import { WithdrawFromLeagueWalletPresenter } from './presenters/WithdrawFromLeagueWalletPresenter';
// Tokens
import {
    APPROVE_LEAGUE_JOIN_REQUEST_USE_CASE,
    CREATE_LEAGUE_SEASON_SCHEDULE_RACE_USE_CASE,
    CREATE_LEAGUE_WITH_SEASON_AND_SCORING_USE_CASE,
    DELETE_LEAGUE_SEASON_SCHEDULE_RACE_USE_CASE,
    GET_ALL_LEAGUES_WITH_CAPACITY_AND_SCORING_USE_CASE,
    GET_ALL_LEAGUES_WITH_CAPACITY_USE_CASE,
    GET_LEAGUE_ADMIN_PERMISSIONS_USE_CASE,
    GET_LEAGUE_FULL_CONFIG_USE_CASE,
    GET_LEAGUE_JOIN_REQUESTS_USE_CASE,
    GET_LEAGUE_MEMBERSHIPS_USE_CASE,
    GET_LEAGUE_OWNER_SUMMARY_USE_CASE,
    GET_LEAGUE_PROTESTS_USE_CASE,
    GET_LEAGUE_ROSTER_JOIN_REQUESTS_USE_CASE,
    GET_LEAGUE_ROSTER_MEMBERS_USE_CASE,
    GET_LEAGUE_SCHEDULE_USE_CASE,
    GET_LEAGUE_SCORING_CONFIG_USE_CASE,
    GET_LEAGUE_SEASONS_USE_CASE,
    GET_LEAGUE_STANDINGS_USE_CASE,
    GET_LEAGUE_STATS_USE_CASE,
    GET_LEAGUE_WALLET_USE_CASE,
    GET_SEASON_SPONSORSHIPS_USE_CASE,
    GET_TOTAL_LEAGUES_USE_CASE,
    JOIN_LEAGUE_USE_CASE,
    LIST_LEAGUE_SCORING_PRESETS_USE_CASE,
    LOGGER_TOKEN,
    PUBLISH_LEAGUE_SEASON_SCHEDULE_USE_CASE,
    REJECT_LEAGUE_JOIN_REQUEST_USE_CASE,
    REMOVE_LEAGUE_MEMBER_USE_CASE,
    TRANSFER_LEAGUE_OWNERSHIP_USE_CASE,
    UNPUBLISH_LEAGUE_SEASON_SCHEDULE_USE_CASE,
    UPDATE_LEAGUE_MEMBER_ROLE_USE_CASE,
    UPDATE_LEAGUE_SEASON_SCHEDULE_RACE_USE_CASE,
    WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE,
} from './LeagueTokens';

@Injectable()
export class LeagueService {
  constructor(
    @Inject(GET_ALL_LEAGUES_WITH_CAPACITY_USE_CASE) private readonly getAllLeaguesWithCapacityUseCase: GetAllLeaguesWithCapacityUseCase,
    @Inject(GET_ALL_LEAGUES_WITH_CAPACITY_AND_SCORING_USE_CASE) private readonly getAllLeaguesWithCapacityAndScoringUseCase: GetAllLeaguesWithCapacityAndScoringUseCase,
    @Inject(GET_LEAGUE_STANDINGS_USE_CASE) private readonly getLeagueStandingsUseCase: GetLeagueStandingsUseCase,
    @Inject(GET_LEAGUE_STATS_USE_CASE) private readonly getLeagueStatsUseCase: GetLeagueStatsUseCase,
    @Inject(GET_LEAGUE_FULL_CONFIG_USE_CASE) private readonly getLeagueFullConfigUseCase: GetLeagueFullConfigUseCase,
    @Inject(GET_LEAGUE_SCORING_CONFIG_USE_CASE) private readonly getLeagueScoringConfigUseCase: GetLeagueScoringConfigUseCase,
    @Inject(LIST_LEAGUE_SCORING_PRESETS_USE_CASE) private readonly listLeagueScoringPresetsUseCase: ListLeagueScoringPresetsUseCase,
    @Inject(JOIN_LEAGUE_USE_CASE) private readonly joinLeagueUseCase: JoinLeagueUseCase,
    @Inject(TRANSFER_LEAGUE_OWNERSHIP_USE_CASE) private readonly transferLeagueOwnershipUseCase: TransferLeagueOwnershipUseCase,
    @Inject(CREATE_LEAGUE_WITH_SEASON_AND_SCORING_USE_CASE) private readonly createLeagueWithSeasonAndScoringUseCase: CreateLeagueWithSeasonAndScoringUseCase,
    @Inject(GET_TOTAL_LEAGUES_USE_CASE) private readonly getTotalLeaguesUseCase: GetTotalLeaguesUseCase,
    @Inject(GET_LEAGUE_JOIN_REQUESTS_USE_CASE) private readonly getLeagueJoinRequestsUseCase: GetLeagueJoinRequestsUseCase,
    @Inject(APPROVE_LEAGUE_JOIN_REQUEST_USE_CASE) private readonly approveLeagueJoinRequestUseCase: ApproveLeagueJoinRequestUseCase,
    @Inject(REJECT_LEAGUE_JOIN_REQUEST_USE_CASE) private readonly rejectLeagueJoinRequestUseCase: RejectLeagueJoinRequestUseCase,
    @Inject(REMOVE_LEAGUE_MEMBER_USE_CASE) private readonly removeLeagueMemberUseCase: RemoveLeagueMemberUseCase,
    @Inject(UPDATE_LEAGUE_MEMBER_ROLE_USE_CASE) private readonly updateLeagueMemberRoleUseCase: UpdateLeagueMemberRoleUseCase,
    @Inject(GET_LEAGUE_OWNER_SUMMARY_USE_CASE) private readonly getLeagueOwnerSummaryUseCase: GetLeagueOwnerSummaryUseCase,
    @Inject(GET_LEAGUE_PROTESTS_USE_CASE) private readonly getLeagueProtestsUseCase: GetLeagueProtestsUseCase,
    @Inject(GET_LEAGUE_SEASONS_USE_CASE) private readonly getLeagueSeasonsUseCase: GetLeagueSeasonsUseCase,
    @Inject(GET_LEAGUE_MEMBERSHIPS_USE_CASE) private readonly getLeagueMembershipsUseCase: GetLeagueMembershipsUseCase,
    @Inject(GET_LEAGUE_SCHEDULE_USE_CASE) private readonly getLeagueScheduleUseCase: GetLeagueScheduleUseCase,
    @Inject(GET_LEAGUE_ADMIN_PERMISSIONS_USE_CASE) private readonly getLeagueAdminPermissionsUseCase: GetLeagueAdminPermissionsUseCase,
    @Inject(GET_LEAGUE_WALLET_USE_CASE) private readonly getLeagueWalletUseCase: GetLeagueWalletUseCase,
    @Inject(WITHDRAW_FROM_LEAGUE_WALLET_USE_CASE) private readonly withdrawFromLeagueWalletUseCase: WithdrawFromLeagueWalletUseCase,
    @Inject(GET_SEASON_SPONSORSHIPS_USE_CASE) private readonly getSeasonSponsorshipsUseCase: GetSeasonSponsorshipsUseCase,

    // Schedule mutations
    @Inject(CREATE_LEAGUE_SEASON_SCHEDULE_RACE_USE_CASE)
    private readonly createLeagueSeasonScheduleRaceUseCase: CreateLeagueSeasonScheduleRaceUseCase,
    @Inject(UPDATE_LEAGUE_SEASON_SCHEDULE_RACE_USE_CASE)
    private readonly updateLeagueSeasonScheduleRaceUseCase: UpdateLeagueSeasonScheduleRaceUseCase,
    @Inject(DELETE_LEAGUE_SEASON_SCHEDULE_RACE_USE_CASE)
    private readonly deleteLeagueSeasonScheduleRaceUseCase: DeleteLeagueSeasonScheduleRaceUseCase,
    @Inject(PUBLISH_LEAGUE_SEASON_SCHEDULE_USE_CASE)
    private readonly publishLeagueSeasonScheduleUseCase: PublishLeagueSeasonScheduleUseCase,
    @Inject(UNPUBLISH_LEAGUE_SEASON_SCHEDULE_USE_CASE)
    private readonly unpublishLeagueSeasonScheduleUseCase: UnpublishLeagueSeasonScheduleUseCase,

    @Inject(LOGGER_TOKEN) private readonly logger: Logger,

    // Injected presenters
    @Inject(AllLeaguesWithCapacityPresenter) private readonly allLeaguesWithCapacityPresenter: AllLeaguesWithCapacityPresenter,
    @Inject(AllLeaguesWithCapacityAndScoringPresenter) private readonly allLeaguesWithCapacityAndScoringPresenter: AllLeaguesWithCapacityAndScoringPresenter,
    @Inject(LeagueStandingsPresenter) private readonly leagueStandingsPresenter: LeagueStandingsPresenter,
    @Inject(GetLeagueProtestsPresenter) private readonly leagueProtestsPresenter: GetLeagueProtestsPresenter,
    @Inject(GetSeasonSponsorshipsPresenter) private readonly seasonSponsorshipsPresenter: GetSeasonSponsorshipsPresenter,
    @Inject(LeagueScoringPresetsPresenter) private readonly leagueScoringPresetsPresenter: LeagueScoringPresetsPresenter,
    @Inject(ApproveLeagueJoinRequestPresenter) private readonly approveLeagueJoinRequestPresenter: ApproveLeagueJoinRequestPresenter,
    @Inject(CreateLeaguePresenter) private readonly createLeaguePresenter: CreateLeaguePresenter,
    @Inject(GetLeagueAdminPermissionsPresenter) private readonly getLeagueAdminPermissionsPresenter: GetLeagueAdminPermissionsPresenter,
    @Inject(GetLeagueMembershipsPresenter) private readonly getLeagueMembershipsPresenter: GetLeagueMembershipsPresenter,
    @Inject(GetLeagueOwnerSummaryPresenter) private readonly getLeagueOwnerSummaryPresenter: GetLeagueOwnerSummaryPresenter,
    @Inject(GetLeagueSeasonsPresenter) private readonly getLeagueSeasonsPresenter: GetLeagueSeasonsPresenter,
    @Inject(JoinLeaguePresenter) private readonly joinLeaguePresenter: JoinLeaguePresenter,
    @Inject(LeagueSchedulePresenter) private readonly leagueSchedulePresenter: LeagueSchedulePresenter,
    @Inject(LeagueStatsPresenter) private readonly leagueStatsPresenter: LeagueStatsPresenter,
    @Inject(RejectLeagueJoinRequestPresenter) private readonly rejectLeagueJoinRequestPresenter: RejectLeagueJoinRequestPresenter,
    @Inject(RemoveLeagueMemberPresenter) private readonly removeLeagueMemberPresenter: RemoveLeagueMemberPresenter,
    @Inject(TotalLeaguesPresenter) private readonly totalLeaguesPresenter: TotalLeaguesPresenter,
    @Inject(TransferLeagueOwnershipPresenter) private readonly transferLeagueOwnershipPresenter: TransferLeagueOwnershipPresenter,
    @Inject(UpdateLeagueMemberRolePresenter) private readonly updateLeagueMemberRolePresenter: UpdateLeagueMemberRolePresenter,
    @Inject(LeagueConfigPresenter) private readonly leagueConfigPresenter: LeagueConfigPresenter,
    @Inject(LeagueScoringConfigPresenter) private readonly leagueScoringConfigPresenter: LeagueScoringConfigPresenter,
    @Inject(GetLeagueWalletPresenter) private readonly getLeagueWalletPresenter: GetLeagueWalletPresenter,
    @Inject(WithdrawFromLeagueWalletPresenter) private readonly withdrawFromLeagueWalletPresenter: WithdrawFromLeagueWalletPresenter,
    @Inject(LeagueJoinRequestsPresenter) private readonly leagueJoinRequestsPresenter: LeagueJoinRequestsPresenter,

    // Schedule mutation presenters
    @Inject(CreateLeagueSeasonScheduleRacePresenter) private readonly createLeagueSeasonScheduleRacePresenter: CreateLeagueSeasonScheduleRacePresenter,
    @Inject(UpdateLeagueSeasonScheduleRacePresenter) private readonly updateLeagueSeasonScheduleRacePresenter: UpdateLeagueSeasonScheduleRacePresenter,
    @Inject(DeleteLeagueSeasonScheduleRacePresenter) private readonly deleteLeagueSeasonScheduleRacePresenter: DeleteLeagueSeasonScheduleRacePresenter,
    @Inject(PublishLeagueSeasonSchedulePresenter) private readonly publishLeagueSeasonSchedulePresenter: PublishLeagueSeasonSchedulePresenter,
    @Inject(UnpublishLeagueSeasonSchedulePresenter) private readonly unpublishLeagueSeasonSchedulePresenter: UnpublishLeagueSeasonSchedulePresenter,

    // Roster admin read delegation
    @Inject(GET_LEAGUE_ROSTER_MEMBERS_USE_CASE)
    private readonly getLeagueRosterMembersUseCase: GetLeagueRosterMembersUseCase,
    @Inject(GET_LEAGUE_ROSTER_JOIN_REQUESTS_USE_CASE)
    private readonly getLeagueRosterJoinRequestsUseCase: GetLeagueRosterJoinRequestsUseCase,
    @Inject(GetLeagueRosterMembersPresenter)
    private readonly getLeagueRosterMembersPresenter: GetLeagueRosterMembersPresenter,
    @Inject(GetLeagueRosterJoinRequestsPresenter)
    private readonly getLeagueRosterJoinRequestsPresenter: GetLeagueRosterJoinRequestsPresenter,
  ) {}

  async getAllLeaguesWithCapacity(): Promise<AllLeaguesWithCapacityViewModel> {
    this.logger.debug('[LeagueService] Fetching all leagues with capacity.');

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

    if (result.isErr()) {
      const err = result.unwrapErr();
      this.logger.error('[LeagueService] Failed to fetch leagues with capacity', new Error(err.code), {
        details: err.details,
      });
      throw new Error(err.code);
    }

    await this.allLeaguesWithCapacityPresenter.present(result.unwrap());
    return this.allLeaguesWithCapacityPresenter.getViewModel();
  }

  async getAllLeaguesWithCapacityAndScoring(): Promise<AllLeaguesWithCapacityAndScoringViewModel> {
    this.logger.debug('[LeagueService] Fetching all leagues with capacity and scoring.');

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

    if (result.isErr()) {
      const err = result.unwrapErr();
      this.logger.error('[LeagueService] Failed to fetch leagues with capacity and scoring', new Error(err.code), {
        details: err.details,
      });
      throw new Error(err.code);
    }

    await this.allLeaguesWithCapacityAndScoringPresenter.present(result.unwrap());
    return this.allLeaguesWithCapacityAndScoringPresenter.getViewModel();
  }

  async getTotalLeagues(): Promise<TotalLeaguesDTO> {
    this.logger.debug('[LeagueService] Fetching total leagues count.');
    const result = await this.getTotalLeaguesUseCase.execute({});
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.totalLeaguesPresenter.present(result.unwrap());
    return this.totalLeaguesPresenter.getResponseModel()!;
  }

  private getActor(): ReturnType<typeof getActorFromRequestContext> {
    return getActorFromRequestContext();
  }

  private async requireLeagueAdminPermissions(leagueId: string): Promise<void> {
    await requireLeagueAdminOrOwner(leagueId, this.getLeagueAdminPermissionsUseCase);
  }

  async getLeagueJoinRequests(leagueId: string): Promise<LeagueJoinRequestWithDriverDTO[]> {
    this.logger.debug(`[LeagueService] Fetching join requests for league: ${leagueId}.`);

    await this.requireLeagueAdminPermissions(leagueId);

    this.leagueJoinRequestsPresenter.reset?.();
    const result = await this.getLeagueJoinRequestsUseCase.execute({ leagueId });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.leagueJoinRequestsPresenter.present(result.unwrap());
    return this.leagueJoinRequestsPresenter.getViewModel()!.joinRequests;
  }

  async approveLeagueJoinRequest(input: ApproveJoinRequestInputDTO): Promise<ApproveLeagueJoinRequestDTO> {
    this.logger.debug('Approving join request:', input);

    await this.requireLeagueAdminPermissions(input.leagueId);

    const result = await this.approveLeagueJoinRequestUseCase.execute(
      { leagueId: input.leagueId, joinRequestId: input.requestId },
    );

    if (result.isErr()) {
      const err = result.unwrapErr();

      if (err.code === 'JOIN_REQUEST_NOT_FOUND') {
        throw new NotFoundException('Join request not found');
      }

      if (err.code === 'LEAGUE_NOT_FOUND') {
        throw new NotFoundException('League not found');
      }

      if (err.code === 'LEAGUE_AT_CAPACITY') {
        throw new ConflictException('League is at capacity');
      }

      throw new Error(err.code);
    }

    await this.approveLeagueJoinRequestPresenter.present(result.unwrap());
    return this.approveLeagueJoinRequestPresenter.getViewModel()!;
  }

  async rejectLeagueJoinRequest(input: RejectJoinRequestInputDTO): Promise<RejectJoinRequestOutputDTO> {
    this.logger.debug('Rejecting join request:', input);

    await this.requireLeagueAdminPermissions(input.leagueId);

    const result = await this.rejectLeagueJoinRequestUseCase.execute(
      { leagueId: input.leagueId, joinRequestId: input.requestId },
    );

    if (result.isErr()) {
      const err = result.unwrapErr();

      if (err.code === 'JOIN_REQUEST_NOT_FOUND') {
        throw new NotFoundException('Join request not found');
      }

      if (err.code === 'LEAGUE_NOT_FOUND') {
        throw new NotFoundException('League not found');
      }

      if (err.code === 'LEAGUE_AT_CAPACITY') {
        throw new ConflictException('League is at capacity');
      }

      throw new Error(err.code);
    }

    await this.rejectLeagueJoinRequestPresenter.present(result.unwrap());
    return this.rejectLeagueJoinRequestPresenter.getViewModel()!;
  }

  async approveLeagueRosterJoinRequest(leagueId: string, joinRequestId: string): Promise<ApproveLeagueJoinRequestDTO> {
    this.logger.debug('Approving roster join request:', { leagueId, joinRequestId });

    await this.requireLeagueAdminPermissions(leagueId);

    const result = await this.approveLeagueJoinRequestUseCase.execute(
      { leagueId, joinRequestId },
    );

    if (result.isErr()) {
      const err = result.unwrapErr();

      if (err.code === 'JOIN_REQUEST_NOT_FOUND') {
        throw new NotFoundException('Join request not found');
      }

      if (err.code === 'LEAGUE_NOT_FOUND') {
        throw new NotFoundException('League not found');
      }

      if (err.code === 'LEAGUE_AT_CAPACITY') {
        throw new ConflictException('League is at capacity');
      }

      throw new Error(err.code);
    }

    await this.approveLeagueJoinRequestPresenter.present(result.unwrap());
    return this.approveLeagueJoinRequestPresenter.getViewModel()!;
  }

  async rejectLeagueRosterJoinRequest(leagueId: string, joinRequestId: string): Promise<RejectJoinRequestOutputDTO> {
    this.logger.debug('Rejecting roster join request:', { leagueId, joinRequestId });

    await this.requireLeagueAdminPermissions(leagueId);

    const result = await this.rejectLeagueJoinRequestUseCase.execute(
      { leagueId, joinRequestId },
    );

    if (result.isErr()) {
      throw new NotFoundException('Join request not found');
    }

    await this.rejectLeagueJoinRequestPresenter.present(result.unwrap());
    return this.rejectLeagueJoinRequestPresenter.getViewModel()!;
  }

  async getLeagueAdminPermissions(query: GetLeagueAdminPermissionsInputDTO): Promise<LeagueAdminPermissionsDTO> {
    const actor = this.getActor();

    this.logger.debug('Getting league admin permissions', { leagueId: query.leagueId, performerDriverId: actor.driverId });

    const result = await this.getLeagueAdminPermissionsUseCase.execute({
      leagueId: query.leagueId,
      performerDriverId: actor.driverId,
    });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.getLeagueAdminPermissionsPresenter.present(result.unwrap());
    return this.getLeagueAdminPermissionsPresenter.getResponseModel()!;
  }

  async removeLeagueMember(input: RemoveLeagueMemberInputDTO): Promise<RemoveLeagueMemberOutputDTO> {
    this.logger.debug('Removing league member', { leagueId: input.leagueId, targetDriverId: input.targetDriverId });

    await this.requireLeagueAdminPermissions(input.leagueId);

    this.removeLeagueMemberPresenter.reset?.();
    const result = await this.removeLeagueMemberUseCase.execute({
      leagueId: input.leagueId,
      targetDriverId: input.targetDriverId,
    });

    if (result.isErr()) {
      const err = result.unwrapErr();

      if (err.code === 'MEMBERSHIP_NOT_FOUND') {
        throw new NotFoundException('Member not found');
      }

      if (err.code === 'CANNOT_REMOVE_LAST_OWNER') {
        throw new BadRequestException(err.details.message);
      }

      throw new Error(err.code);
    }

    await this.removeLeagueMemberPresenter.present(result.unwrap());
    return this.removeLeagueMemberPresenter.getViewModel()!;
  }

  async updateLeagueMemberRole(
    leagueId: string,
    targetDriverId: string,
    input: UpdateLeagueMemberRoleInputDTO,
  ): Promise<UpdateLeagueMemberRoleOutputDTO> {
    this.logger.debug('Updating league member role', {
      leagueId,
      targetDriverId,
      newRole: input.newRole,
    });

    await this.requireLeagueAdminPermissions(leagueId);

    this.updateLeagueMemberRolePresenter.reset?.();
    const result = await this.updateLeagueMemberRoleUseCase.execute({
      leagueId,
      targetDriverId,
      newRole: input.newRole,
    });

    if (result.isErr()) {
      const err = result.unwrapErr();

      if (err.code === 'MEMBERSHIP_NOT_FOUND') {
        throw new NotFoundException('Member not found');
      }

      if (err.code === 'INVALID_ROLE' || err.code === 'CANNOT_DOWNGRADE_LAST_OWNER') {
        throw new BadRequestException(err.details.message);
      }

      throw new Error(err.code);
    }

    await this.updateLeagueMemberRolePresenter.present(result.unwrap());
    return this.updateLeagueMemberRolePresenter.getViewModel()!;
  }

  async getLeagueOwnerSummary(query: GetLeagueOwnerSummaryQueryDTO): Promise<LeagueOwnerSummaryDTO> {
    this.logger.debug('Getting league owner summary:', query);
    const result = await this.getLeagueOwnerSummaryUseCase.execute(query);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.getLeagueOwnerSummaryPresenter.present(result.unwrap());
    return this.getLeagueOwnerSummaryPresenter.getViewModel()!;
  }

  async getLeagueFullConfig(query: GetLeagueAdminConfigQueryDTO): Promise<LeagueConfigFormModelDTO | null> {
    this.logger.debug('Getting league full config', { query });

    const result = await this.getLeagueFullConfigUseCase.execute(query);
    if (result.isErr()) {
      return null;
    }
    
    await this.leagueConfigPresenter.present(result.unwrap());
    return this.leagueConfigPresenter.getViewModel();
  }

  async getLeagueProtests(query: GetLeagueProtestsQueryDTO): Promise<LeagueAdminProtestsDTO> {
    this.logger.debug('Getting league protests:', query);
    const result = await this.getLeagueProtestsUseCase.execute(query);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.leagueProtestsPresenter.present(result.unwrap());
    return this.leagueProtestsPresenter.getResponseModel()!;
  }

  async getLeagueSeasons(query: GetLeagueSeasonsQueryDTO): Promise<LeagueSeasonSummaryDTO[]> {
    this.logger.debug('Getting league seasons:', query);
    const result = await this.getLeagueSeasonsUseCase.execute(query);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.getLeagueSeasonsPresenter.present(result.unwrap());
    return this.getLeagueSeasonsPresenter.getResponseModel()!;
  }

  async getLeagueMemberships(leagueId: string): Promise<LeagueMembershipsDTO> {
    this.logger.debug('Getting league memberships', { leagueId });
    const result = await this.getLeagueMembershipsUseCase.execute({ leagueId });
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.getLeagueMembershipsPresenter.present(result.unwrap());
    return this.getLeagueMembershipsPresenter.getViewModel()!.memberships;
  }

  async getLeagueRosterMembers(leagueId: string): Promise<LeagueRosterMemberDTO[]> {
    this.logger.debug('Getting league roster members (admin)', { leagueId });

    await this.requireLeagueAdminPermissions(leagueId);

    this.getLeagueRosterMembersPresenter.reset?.();
    const result = await this.getLeagueRosterMembersUseCase.execute({ leagueId });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.getLeagueRosterMembersPresenter.present(result.unwrap());
    return this.getLeagueRosterMembersPresenter.getViewModel()!;
  }

  async getLeagueRosterJoinRequests(leagueId: string): Promise<LeagueRosterJoinRequestDTO[]> {
    this.logger.debug('Getting league roster join requests (admin)', { leagueId });

    await this.requireLeagueAdminPermissions(leagueId);

    this.getLeagueRosterJoinRequestsPresenter.reset?.();
    const result = await this.getLeagueRosterJoinRequestsUseCase.execute({ leagueId });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.getLeagueRosterJoinRequestsPresenter.present(result.unwrap());
    return this.getLeagueRosterJoinRequestsPresenter.getViewModel()!;
  }

  async getLeagueStandings(leagueId: string): Promise<LeagueStandingsDTO> {
    this.logger.debug('Getting league standings', { leagueId });
    const result = await this.getLeagueStandingsUseCase.execute({ leagueId });
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.leagueStandingsPresenter.present(result.unwrap());
    return this.leagueStandingsPresenter.getResponseModel()!;
  }

  async getLeagueSchedule(leagueId: string, query?: GetLeagueScheduleQueryDTO): Promise<LeagueScheduleDTO> {
    this.logger.debug('Getting league schedule', { leagueId, query });

    const input: GetLeagueScheduleInput = query?.seasonId ? { leagueId, seasonId: query.seasonId } : { leagueId };
    const result = await this.getLeagueScheduleUseCase.execute(input);

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.leagueSchedulePresenter.present(result.unwrap());
    return this.leagueSchedulePresenter.getViewModel()!;
  }

  async publishLeagueSeasonSchedule(
    leagueId: string,
    seasonId: string,
    _input: LeagueSeasonSchedulePublishInputDTO,
  ): Promise<LeagueSeasonSchedulePublishOutputDTO> {
    void _input;

    await this.requireLeagueAdminPermissions(leagueId);

    this.publishLeagueSeasonSchedulePresenter.reset?.();
    const result = await this.publishLeagueSeasonScheduleUseCase.execute({ leagueId, seasonId });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.publishLeagueSeasonSchedulePresenter.present(result.unwrap());
    return this.publishLeagueSeasonSchedulePresenter.getResponseModel()!;
  }

  async unpublishLeagueSeasonSchedule(
    leagueId: string,
    seasonId: string,
    _input: LeagueSeasonSchedulePublishInputDTO,
  ): Promise<LeagueSeasonSchedulePublishOutputDTO> {
    void _input;

    await this.requireLeagueAdminPermissions(leagueId);

    this.unpublishLeagueSeasonSchedulePresenter.reset?.();
    const result = await this.unpublishLeagueSeasonScheduleUseCase.execute({ leagueId, seasonId });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.unpublishLeagueSeasonSchedulePresenter.present(result.unwrap());
    return this.unpublishLeagueSeasonSchedulePresenter.getResponseModel()!;
  }

  async createLeagueSeasonScheduleRace(
    leagueId: string,
    seasonId: string,
    input: CreateLeagueScheduleRaceInputDTO,
  ): Promise<CreateLeagueScheduleRaceOutputDTO> {
    await this.requireLeagueAdminPermissions(leagueId);

    const scheduledAt = new Date(input.scheduledAtIso);
    if (Number.isNaN(scheduledAt.getTime())) {
      throw new Error('INVALID_SCHEDULED_AT');
    }

    this.createLeagueSeasonScheduleRacePresenter.reset?.();
    const result = await this.createLeagueSeasonScheduleRaceUseCase.execute({
      leagueId,
      seasonId,
      track: input.track,
      car: input.car,
      scheduledAt,
    });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.createLeagueSeasonScheduleRacePresenter.present(result.unwrap());
    return this.createLeagueSeasonScheduleRacePresenter.getResponseModel()!;
  }

  async updateLeagueSeasonScheduleRace(
    leagueId: string,
    seasonId: string,
    raceId: string,
    input: UpdateLeagueScheduleRaceInputDTO,
  ): Promise<LeagueScheduleRaceMutationSuccessDTO> {
    await this.requireLeagueAdminPermissions(leagueId);

    const scheduledAt =
      input.scheduledAtIso !== undefined ? new Date(input.scheduledAtIso) : undefined;

    if (scheduledAt && Number.isNaN(scheduledAt.getTime())) {
      throw new Error('INVALID_SCHEDULED_AT');
    }

    this.updateLeagueSeasonScheduleRacePresenter.reset?.();
    const result = await this.updateLeagueSeasonScheduleRaceUseCase.execute({
      leagueId,
      seasonId,
      raceId,
      ...(input.track !== undefined ? { track: input.track } : {}),
      ...(input.car !== undefined ? { car: input.car } : {}),
      ...(scheduledAt !== undefined ? { scheduledAt } : {}),
    });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.updateLeagueSeasonScheduleRacePresenter.present(result.unwrap());
    return this.updateLeagueSeasonScheduleRacePresenter.getResponseModel()!;
  }

  async deleteLeagueSeasonScheduleRace(
    leagueId: string,
    seasonId: string,
    raceId: string,
  ): Promise<LeagueScheduleRaceMutationSuccessDTO> {
    await this.requireLeagueAdminPermissions(leagueId);

    this.deleteLeagueSeasonScheduleRacePresenter.reset?.();
    const result = await this.deleteLeagueSeasonScheduleRaceUseCase.execute({ leagueId, seasonId, raceId });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.deleteLeagueSeasonScheduleRacePresenter.present(result.unwrap());
    return this.deleteLeagueSeasonScheduleRacePresenter.getResponseModel()!;
  }

  async getLeagueStats(leagueId: string): Promise<LeagueStatsDTO> {
    this.logger.debug('Getting league stats', { leagueId });
    const result = await this.getLeagueStatsUseCase.execute({ leagueId });
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.leagueStatsPresenter.present(result.unwrap());
    return this.leagueStatsPresenter.getResponseModel()!;
  }

  async getLeagueAdmin(leagueId: string): Promise<LeagueAdminDTO> {
    this.logger.debug('Getting league admin data', { leagueId });
    
    const [fullConfigResult, joinRequests, protests, seasons] = await Promise.all([
      this.getLeagueFullConfigUseCase.execute({ leagueId }),
      this.getLeagueJoinRequests(leagueId),
      this.getLeagueProtests({ leagueId }),
      this.getLeagueSeasons({ leagueId }),
    ]);

    if (fullConfigResult.isErr()) {
      throw new Error(fullConfigResult.unwrapErr().code);
    }

    // Present the full config result
    await this.leagueConfigPresenter.present(fullConfigResult.unwrap());

    const ownerSummaryResult = await this.getLeagueOwnerSummaryUseCase.execute({ leagueId });
    if (ownerSummaryResult.isErr()) {
      throw new Error(ownerSummaryResult.unwrapErr().code);
    }
    await this.getLeagueOwnerSummaryPresenter.present(ownerSummaryResult.unwrap());
    const ownerSummary = this.getLeagueOwnerSummaryPresenter.getViewModel()!;

    const configForm = this.leagueConfigPresenter.getViewModel();

    // For now, return a simple structure since we don't have a LeagueAdminPresenter
    return {
      joinRequests: joinRequests,
      ownerSummary,
      config: { form: configForm },
      protests,
      seasons,
    };
  }

  async createLeague(input: CreateLeagueInputDTO): Promise<CreateLeagueViewModel> {
    this.logger.debug('Creating league', { input });
    const command = {
      name: input.name,
      description: input.description,
      ownerId: input.ownerId,
      visibility: 'unranked' as const,
      gameId: 'iracing', // Assume default
      maxDrivers: 32, // Default value
      enableDriverChampionship: true,
      enableTeamChampionship: false,
      enableNationsChampionship: false,
      enableTrophyChampionship: false,
    };
    const result = await this.createLeagueWithSeasonAndScoringUseCase.execute(command);
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.createLeaguePresenter.present(result.unwrap());
    return this.createLeaguePresenter.getViewModel()!;
  }

  async getLeagueScoringConfig(leagueId: string): Promise<LeagueScoringConfigViewModel | null> {
    this.logger.debug('Getting league scoring config', { leagueId });

    try {
      const result = await this.getLeagueScoringConfigUseCase.execute({ leagueId });
      if (result.isErr()) {
        throw new Error(result.unwrapErr().code);
      }
      await this.leagueScoringConfigPresenter.present(result.unwrap());
      return this.leagueScoringConfigPresenter.getViewModel();
    } catch (error) {
      this.logger.error('Error getting league scoring config', error instanceof Error ? error : new Error(String(error)));
      return null;
    }
  }

  async listLeagueScoringPresets(): Promise<LeagueScoringPresetsViewModel> {
    this.logger.debug('Listing league scoring presets');

    const result = await this.listLeagueScoringPresetsUseCase.execute({});
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.leagueScoringPresetsPresenter.present(result.unwrap());
    return this.leagueScoringPresetsPresenter.getViewModel()!;
  }

  async joinLeague(leagueId: string): Promise<JoinLeagueOutputDTO> {
    const actor = this.getActor();
    this.logger.debug('Joining league', { leagueId, actorDriverId: actor.driverId });

    const result = await this.joinLeagueUseCase.execute({ leagueId, driverId: actor.driverId });
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.joinLeaguePresenter.present(result.unwrap());
    return this.joinLeaguePresenter.getViewModel()!;
  }

  async transferLeagueOwnership(leagueId: string, input: TransferLeagueOwnershipInputDTO): Promise<TransferLeagueOwnershipOutputDTO> {
    this.logger.debug('Transferring league ownership', { leagueId, newOwnerId: input.newOwnerId });

    await this.requireLeagueAdminPermissions(leagueId);

    const actor = this.getActor();

    const result = await this.transferLeagueOwnershipUseCase.execute({
      leagueId,
      currentOwnerId: actor.driverId,
      newOwnerId: input.newOwnerId,
    });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.transferLeagueOwnershipPresenter.present(result.unwrap());
    return this.transferLeagueOwnershipPresenter.getViewModel()!;
  }

  async getSeasonSponsorships(seasonId: string): Promise<GetSeasonSponsorshipsOutputDTO> {
    this.logger.debug('Getting season sponsorships', { seasonId });

    const result = await this.getSeasonSponsorshipsUseCase.execute({ seasonId });
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.seasonSponsorshipsPresenter.present(result.unwrap());
    return this.seasonSponsorshipsPresenter.getViewModel()!;
  }
  
  async getRaces(leagueId: string): Promise<GetLeagueRacesOutputDTO> {
    this.logger.debug('Getting league races', { leagueId });

    // `GetLeagueScheduleUseCase` is wired to `LeagueSchedulePresenter` (not `LeagueRacesPresenter`),
    // so `LeagueRacesPresenter.getViewModel()` can be null at runtime.
    this.leagueSchedulePresenter.reset?.();
    const result = await this.getLeagueScheduleUseCase.execute({ leagueId });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.leagueSchedulePresenter.present(result.unwrap());
    return {
      races: this.leagueSchedulePresenter.getViewModel()?.races ?? [],
    };
  }

  async getLeagueWallet(leagueId: string): Promise<GetLeagueWalletOutputDTO> {
    this.logger.debug('Getting league wallet', { leagueId });
    const result = await this.getLeagueWalletUseCase.execute({ leagueId });
    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }
    await this.getLeagueWalletPresenter.present(result.unwrap());
    return this.getLeagueWalletPresenter.getResponseModel();
  }

  async withdrawFromLeagueWallet(
    leagueId: string,
    input: WithdrawFromLeagueWalletInputDTO,
  ): Promise<WithdrawFromLeagueWalletOutputDTO> {
    this.logger.debug('Withdrawing from league wallet', { leagueId, amount: input.amount });

    const actor = this.getActor();

    const result = await this.withdrawFromLeagueWalletUseCase.execute({
      leagueId,
      requestedById: actor.driverId,
      amount: input.amount,
      currency: input.currency as 'USD' | 'EUR' | 'GBP',
      reason: input.destinationAccount,
    });

    if (result.isErr()) {
      throw new Error(result.unwrapErr().code);
    }

    await this.withdrawFromLeagueWalletPresenter.present(result.unwrap());
    return this.withdrawFromLeagueWalletPresenter.getResponseModel();
  }
}