import { describe, it, expect, vi, type Mock } from 'vitest';
import { GetPrizesUseCase, type GetPrizesInput } from './GetPrizesUseCase';
import type { IPrizeRepository } from '../../domain/repositories/IPrizeRepository';
import { PrizeType, type Prize } from '../../domain/entities/Prize';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';

describe('GetPrizesUseCase', () => {
  let prizeRepository: {
    findByLeagueId: Mock;
    findByLeagueIdAndSeasonId: Mock;
  };
  let output: { present: Mock };
  let useCase: GetPrizesUseCase;

  beforeEach(() => {
    prizeRepository = {
      findByLeagueId: vi.fn(),
      findByLeagueIdAndSeasonId: vi.fn(),
    };

    output = {
      present: vi.fn(),
    };

    useCase = new GetPrizesUseCase(
      prizeRepository as unknown as IPrizeRepository,
      output as unknown as UseCaseOutputPort<unknown>,
    );
  });

  it('retrieves and sorts prizes by leagueId when seasonId is not provided', async () => {
    const prizes: Prize[] = [
      {
        id: 'p2',
        leagueId: 'league-1',
        seasonId: 'season-1',
        position: 2,
        name: 'Second',
        amount: 50,
        type: PrizeType.CASH,
        awarded: false,
        createdAt: new Date(),
      },
      {
        id: 'p1',
        leagueId: 'league-1',
        seasonId: 'season-1',
        position: 1,
        name: 'First',
        amount: 100,
        type: PrizeType.CASH,
        awarded: false,
        createdAt: new Date(),
      },
    ];
    prizeRepository.findByLeagueId.mockResolvedValue(prizes);

    const input: GetPrizesInput = { leagueId: 'league-1' };
    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    expect(prizeRepository.findByLeagueId).toHaveBeenCalledWith('league-1');
    expect(prizeRepository.findByLeagueIdAndSeasonId).not.toHaveBeenCalled();

    expect(output.present).toHaveBeenCalledTimes(1);
    const presented = (output.present as unknown as Mock).mock.calls[0]![0]!.prizes as Prize[];
    expect(presented.map(p => p.position)).toEqual([1, 2]);
    expect(presented.map(p => p.id)).toEqual(['p1', 'p2']);
  });

  it('retrieves and sorts prizes by leagueId and seasonId when provided', async () => {
    const prizes: Prize[] = [
      {
        id: 'p3',
        leagueId: 'league-1',
        seasonId: 'season-1',
        position: 3,
        name: 'Third',
        amount: 25,
        type: PrizeType.CASH,
        awarded: false,
        createdAt: new Date(),
      },
      {
        id: 'p1',
        leagueId: 'league-1',
        seasonId: 'season-1',
        position: 1,
        name: 'First',
        amount: 100,
        type: PrizeType.CASH,
        awarded: false,
        createdAt: new Date(),
      },
    ];
    prizeRepository.findByLeagueIdAndSeasonId.mockResolvedValue(prizes);

    const input: GetPrizesInput = { leagueId: 'league-1', seasonId: 'season-1' };
    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    expect(prizeRepository.findByLeagueIdAndSeasonId).toHaveBeenCalledWith('league-1', 'season-1');
    expect(prizeRepository.findByLeagueId).not.toHaveBeenCalled();

    expect(output.present).toHaveBeenCalledTimes(1);
    const presented = (output.present as unknown as Mock).mock.calls[0]![0]!.prizes as Prize[];
    expect(presented.map(p => p.position)).toEqual([1, 3]);
    expect(presented.map(p => p.id)).toEqual(['p1', 'p3']);
  });
});