import { Result } from '@core/shared/domain/Result';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { RaceService } from './RaceService';

describe('RaceService', () => {
  it('invokes each use case and returns the corresponding presenter', async () => {
    // Mock use cases to return Result.ok()
    const mkUseCase = (resultValue: unknown = { success: true }) => ({
      execute: vi.fn(async () => Result.ok(resultValue))
    });

    const getAllRacesUseCase = mkUseCase({ races: [], leagues: [] });
    const getTotalRacesUseCase = mkUseCase({ totalRaces: 0 });
    const importRaceResultsApiUseCase = mkUseCase({ success: true, raceId: 'r1', driversProcessed: 0, resultsRecorded: 0, errors: [] });
    const getRaceDetailUseCase = mkUseCase({ race: null, league: null, drivers: [], isUserRegistered: false, canRegister: false });
    const getRacesPageDataUseCase = mkUseCase({ races: [] });
    const getAllRacesPageDataUseCase = mkUseCase({ races: [], filters: { statuses: [], leagues: [] } });
    const getRaceResultsDetailUseCase = mkUseCase({ race: null, results: [], penalties: [] });
    const getRaceWithSOFUseCase = mkUseCase({ race: null, strengthOfField: 0, participantCount: 0, registeredCount: 0, maxParticipants: 0 });
    const getRaceProtestsUseCase = mkUseCase({ protests: [], drivers: [] });
    const getRacePenaltiesUseCase = mkUseCase({ penalties: [], drivers: [] });
    const registerForRaceUseCase = mkUseCase();
    const withdrawFromRaceUseCase = mkUseCase();
    const cancelRaceUseCase = mkUseCase();
    const completeRaceUseCase = mkUseCase();
    const fileProtestUseCase = mkUseCase();
    const quickPenaltyUseCase = mkUseCase();
    const applyPenaltyUseCase = mkUseCase();
    const requestProtestDefenseUseCase = mkUseCase();
    const reviewProtestUseCase = mkUseCase();
    const reopenRaceUseCase = mkUseCase();

    const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };

    const getAllRacesPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const getTotalRacesPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const importRaceResultsApiPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const raceDetailPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const racesPageDataPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const allRacesPageDataPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const raceResultsDetailPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const raceWithSOFPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const raceProtestsPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const racePenaltiesPresenter = { present: vi.fn() } as unknown as { present: Mock };
    const commandResultPresenter = { present: vi.fn() } as unknown as { present: Mock };

    const service = new RaceService(
      getAllRacesUseCase as never,
      getTotalRacesUseCase as never,
      importRaceResultsApiUseCase as never,
      getRaceDetailUseCase as never,
      getRacesPageDataUseCase as never,
      getAllRacesPageDataUseCase as never,
      getRaceResultsDetailUseCase as never,
      getRaceWithSOFUseCase as never,
      getRaceProtestsUseCase as never,
      getRacePenaltiesUseCase as never,
      registerForRaceUseCase as never,
      withdrawFromRaceUseCase as never,
      cancelRaceUseCase as never,
      completeRaceUseCase as never,
      fileProtestUseCase as never,
      quickPenaltyUseCase as never,
      applyPenaltyUseCase as never,
      requestProtestDefenseUseCase as never,
      reviewProtestUseCase as never,
      reopenRaceUseCase as never,
      logger as never,
      getAllRacesPresenter as never,
      getTotalRacesPresenter as never,
      importRaceResultsApiPresenter as never,
      raceDetailPresenter as never,
      racesPageDataPresenter as never,
      allRacesPageDataPresenter as never,
      raceResultsDetailPresenter as never,
      raceWithSOFPresenter as never,
      raceProtestsPresenter as never,
      racePenaltiesPresenter as never,
      commandResultPresenter as never,
    );

    expect(await service.getAllRaces()).toBe(getAllRacesPresenter);
    expect(getAllRacesUseCase.execute).toHaveBeenCalledWith({});
    expect(getAllRacesPresenter.present).toHaveBeenCalledWith({ races: [], leagues: [] });

    expect(await service.getTotalRaces()).toBe(getTotalRacesPresenter);
    expect(getTotalRacesUseCase.execute).toHaveBeenCalledWith({});
    expect(getTotalRacesPresenter.present).toHaveBeenCalledWith({ totalRaces: 0 });

    expect(await service.importRaceResults({ raceId: 'r1', resultsFileContent: 'x' } as never)).toBe(importRaceResultsApiPresenter);
    expect(importRaceResultsApiUseCase.execute).toHaveBeenCalledWith({ raceId: 'r1', resultsFileContent: 'x' });
    expect(importRaceResultsApiPresenter.present).toHaveBeenCalledWith({ success: true, raceId: 'r1', driversProcessed: 0, resultsRecorded: 0, errors: [] });

    expect(await service.getRaceDetail({ raceId: 'r1' } as never)).toBe(raceDetailPresenter);
    expect(getRaceDetailUseCase.execute).toHaveBeenCalled();

    expect(await service.getRacesPageData('l1')).toBe(racesPageDataPresenter);
    expect(getRacesPageDataUseCase.execute).toHaveBeenCalledWith({ leagueId: 'l1' });
    expect(racesPageDataPresenter.present).toHaveBeenCalledWith({ races: [] });

    expect(await service.getAllRacesPageData()).toBe(allRacesPageDataPresenter);
    expect(getAllRacesPageDataUseCase.execute).toHaveBeenCalledWith({});
    expect(allRacesPageDataPresenter.present).toHaveBeenCalledWith({ races: [], filters: { statuses: [], leagues: [] } });

    expect(await service.getRaceResultsDetail('r1')).toBe(raceResultsDetailPresenter);
    expect(getRaceResultsDetailUseCase.execute).toHaveBeenCalledWith({ raceId: 'r1' });
    expect(raceResultsDetailPresenter.present).toHaveBeenCalledWith({ race: null, results: [], penalties: [] });

    expect(await service.getRaceWithSOF('r1')).toBe(raceWithSOFPresenter);
    expect(getRaceWithSOFUseCase.execute).toHaveBeenCalledWith({ raceId: 'r1' });
    expect(raceWithSOFPresenter.present).toHaveBeenCalledWith({ race: null, strengthOfField: 0, participantCount: 0, registeredCount: 0, maxParticipants: 0 });

    expect(await service.getRaceProtests('r1')).toBe(raceProtestsPresenter);
    expect(getRaceProtestsUseCase.execute).toHaveBeenCalledWith({ raceId: 'r1' });
    expect(raceProtestsPresenter.present).toHaveBeenCalledWith({ protests: [], drivers: [] });

    expect(await service.getRacePenalties('r1')).toBe(racePenaltiesPresenter);
    expect(getRacePenaltiesUseCase.execute).toHaveBeenCalledWith({ raceId: 'r1' });
    expect(racePenaltiesPresenter.present).toHaveBeenCalledWith({ penalties: [], drivers: [] });

    expect(await service.registerForRace({ raceId: 'r1', driverId: 'd1' } as never)).toBe(commandResultPresenter);
    expect(registerForRaceUseCase.execute).toHaveBeenCalled();
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.withdrawFromRace({ raceId: 'r1', driverId: 'd1' } as never)).toBe(commandResultPresenter);
    expect(withdrawFromRaceUseCase.execute).toHaveBeenCalled();
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.cancelRace({ raceId: 'r1' } as never, 'admin')).toBe(commandResultPresenter);
    expect(cancelRaceUseCase.execute).toHaveBeenCalledWith({ raceId: 'r1', cancelledById: 'admin' });
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.completeRace({ raceId: 'r1' } as never)).toBe(commandResultPresenter);
    expect(completeRaceUseCase.execute).toHaveBeenCalledWith({ raceId: 'r1' });
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.reopenRace({ raceId: 'r1' } as never, 'admin')).toBe(commandResultPresenter);
    expect(reopenRaceUseCase.execute).toHaveBeenCalledWith({ raceId: 'r1', reopenedById: 'admin' });
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.fileProtest({} as never)).toBe(commandResultPresenter);
    expect(fileProtestUseCase.execute).toHaveBeenCalled();
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.applyQuickPenalty({} as never)).toBe(commandResultPresenter);
    expect(quickPenaltyUseCase.execute).toHaveBeenCalled();
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.applyPenalty({} as never)).toBe(commandResultPresenter);
    expect(applyPenaltyUseCase.execute).toHaveBeenCalled();
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.requestProtestDefense({} as never)).toBe(commandResultPresenter);
    expect(requestProtestDefenseUseCase.execute).toHaveBeenCalled();
    expect(commandResultPresenter.present).toHaveBeenCalled();

    expect(await service.reviewProtest({} as never)).toBe(commandResultPresenter);
    expect(reviewProtestUseCase.execute).toHaveBeenCalled();
    expect(commandResultPresenter.present).toHaveBeenCalled();
  });
});
