import { describe, it, expect, vi, type Mock } from 'vitest';
import { GetMembershipFeesUseCase, type GetMembershipFeesInput } from './GetMembershipFeesUseCase';
import type { IMembershipFeeRepository, IMemberPaymentRepository } from '../../domain/repositories/IMembershipFeeRepository';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';

describe('GetMembershipFeesUseCase', () => {
  let membershipFeeRepository: {
    findByLeagueId: Mock;
  };
  let memberPaymentRepository: {
    findByLeagueIdAndDriverId: Mock;
  };
  let output: {
    present: Mock;
  };
  let useCase: GetMembershipFeesUseCase;

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

    memberPaymentRepository = {
      findByLeagueIdAndDriverId: vi.fn(),
    };

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

    useCase = new GetMembershipFeesUseCase(
      membershipFeeRepository as unknown as IMembershipFeeRepository,
      memberPaymentRepository as unknown as IMemberPaymentRepository,
      output as unknown as UseCaseOutputPort<unknown>,
    );
  });

  it('returns error when leagueId is missing', async () => {
    const input = { leagueId: '' } as GetMembershipFeesInput;

    const result = await useCase.execute(input);

    expect(result.isErr()).toBe(true);
    expect(result.unwrapErr().code).toBe('INVALID_INPUT');
  });

  it('returns null fee and empty payments when no fee exists', async () => {
    const input: GetMembershipFeesInput = { leagueId: 'league-1' };

    membershipFeeRepository.findByLeagueId.mockResolvedValue(null);

    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    expect(membershipFeeRepository.findByLeagueId).toHaveBeenCalledWith('league-1');
    expect(memberPaymentRepository.findByLeagueIdAndDriverId).not.toHaveBeenCalled();
    expect(output.present).toHaveBeenCalledWith({
      fee: null,
      payments: [],
    });
  });

  it('maps fee and payments when fee and driverId are provided', async () => {
    const input: GetMembershipFeesInput = { leagueId: 'league-1', driverId: 'driver-1' };

    const fee = {
      id: 'fee-1',
      leagueId: 'league-1',
      seasonId: 'season-1',
      type: 'season',
      amount: 100,
      enabled: true,
      createdAt: new Date('2024-01-01'),
      updatedAt: new Date('2024-01-02'),
    };

    const payments = [
      {
        id: 'pay-1',
        feeId: 'fee-1',
        driverId: 'driver-1',
        amount: 100,
        platformFee: 5,
        netAmount: 95,
        status: 'paid',
        dueDate: new Date('2024-02-01'),
        paidAt: new Date('2024-01-15'),
      },
    ];

    membershipFeeRepository.findByLeagueId.mockResolvedValue(fee);
    memberPaymentRepository.findByLeagueIdAndDriverId.mockResolvedValue(payments);

    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    expect(membershipFeeRepository.findByLeagueId).toHaveBeenCalledWith('league-1');
    expect(memberPaymentRepository.findByLeagueIdAndDriverId).toHaveBeenCalledWith('league-1', 'driver-1', membershipFeeRepository as unknown as IMembershipFeeRepository);

    expect(output.present).toHaveBeenCalledWith({
      fee,
      payments,
    });
  });
});
