import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import { Driver } from '../../domain/entities/Driver';
import { Standing } from '../../domain/entities/Standing';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { StandingRepository } from '../../domain/repositories/StandingRepository';
import {
    GetLeagueStandingsUseCase,
    type GetLeagueStandingsErrorCode,
    type GetLeagueStandingsInput,
} from './GetLeagueStandingsUseCase';

describe('GetLeagueStandingsUseCase', () => {
  let useCase: GetLeagueStandingsUseCase;
  let standingRepository: {
    findByLeagueId: Mock;
  };
  let driverRepository: {
    findById: Mock;
  };

  beforeEach(() => {
    standingRepository = {
      findByLeagueId: vi.fn(),
    };
    driverRepository = {
      findById: vi.fn(),
    };

    useCase = new GetLeagueStandingsUseCase(standingRepository as unknown as StandingRepository,
      driverRepository as unknown as DriverRepository);
  });

  it('should return standings with drivers mapped', async () => {
    const leagueId = 'league-1';
    const standings = [
      Standing.create({
        id: 'standing-1',
        leagueId,
        driverId: 'driver-1',
        points: 100,
        position: 1,
      }),
      Standing.create({
        id: 'standing-2',
        leagueId,
        driverId: 'driver-2',
        points: 80,
        position: 2,
      }),
    ];
    const driver1 = Driver.create({
      id: 'driver-1',
      iracingId: '123',
      name: 'Driver One',
      country: 'US',
    });
    const driver2 = Driver.create({
      id: 'driver-2',
      iracingId: '456',
      name: 'Driver Two',
      country: 'US',
    });

    standingRepository.findByLeagueId.mockResolvedValue(standings);
    driverRepository.findById.mockImplementation((id: string) => {
      if (id === 'driver-1') return Promise.resolve(driver1);
      if (id === 'driver-2') return Promise.resolve(driver2);
      return Promise.resolve(null);
    });

    const result = await useCase.execute({ leagueId } satisfies GetLeagueStandingsInput);

    expect(result.isOk()).toBe(true);
    const presented = result.unwrap();

    expect(presented.standings).toHaveLength(2);
    expect(presented.standings[0]).toEqual({
      driverId: 'driver-1',
      driver: driver1,
      points: 100,
      rank: 1,
    });
    expect(presented.standings[1]).toEqual({
      driverId: 'driver-2',
      driver: driver2,
      points: 80,
      rank: 2,
    });
  });

  it('should return repository error when repository fails', async () => {
    const leagueId = 'league-1';
    standingRepository.findByLeagueId.mockRejectedValue(new Error('DB error'));

    const result = await useCase.execute({ leagueId } satisfies GetLeagueStandingsInput);

    expect(result.isErr()).toBe(true);
    const error = result.unwrapErr() as ApplicationErrorCode<
      GetLeagueStandingsErrorCode,
      { message: string }
    >;

    expect(error.code).toBe('REPOSITORY_ERROR');
    expect(error.details.message).toBe('DB error');
  });
});
