import { describe, it, expect, vi, type Mock } from 'vitest';
import { ProcessWalletTransactionUseCase, type ProcessWalletTransactionInput } from './ProcessWalletTransactionUseCase';
import type { IWalletRepository, ITransactionRepository } from '../../domain/repositories/IWalletRepository';
import { TransactionType, ReferenceType } from '../../domain/entities/Wallet';
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';

describe('ProcessWalletTransactionUseCase', () => {
  let walletRepository: {
    findByLeagueId: Mock;
    create: Mock;
    update: Mock;
  };
  let transactionRepository: {
    create: Mock;
  };
  let output: {
    present: Mock;
  };
  let useCase: ProcessWalletTransactionUseCase;

  beforeEach(() => {
    walletRepository = {
      findByLeagueId: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
    };

    transactionRepository = {
      create: vi.fn(),
    };

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

    useCase = new ProcessWalletTransactionUseCase(
      walletRepository as unknown as IWalletRepository,
      transactionRepository as unknown as ITransactionRepository,
      output as unknown as UseCaseOutputPort<unknown>,
    );
  });

  it('processes a deposit transaction and presents the result', async () => {
    const input: ProcessWalletTransactionInput = {
      leagueId: 'league-1',
      type: TransactionType.DEPOSIT,
      amount: 100,
      description: 'Test deposit',
      referenceId: 'ref-1',
      referenceType: ReferenceType.SPONSORSHIP,
    };

    const wallet = {
      id: 'wallet-1',
      leagueId: 'league-1',
      balance: 50,
      totalRevenue: 50,
      totalPlatformFees: 0,
      totalWithdrawn: 0,
      currency: 'USD',
      createdAt: new Date(),
    };

    const transaction = {
      id: 'txn-1',
      walletId: 'wallet-1',
      type: TransactionType.DEPOSIT,
      amount: 100,
      description: 'Test deposit',
      referenceId: 'ref-1',
      referenceType: ReferenceType.SPONSORSHIP,
      createdAt: new Date(),
    };

    walletRepository.findByLeagueId.mockResolvedValue(wallet);
    transactionRepository.create.mockResolvedValue(transaction);
    walletRepository.update.mockResolvedValue({ ...wallet, balance: 150, totalRevenue: 150 });

    const result = await useCase.execute(input);

    expect(result.isOk()).toBe(true);
    expect(output.present).toHaveBeenCalledWith({
      wallet: { ...wallet, balance: 150, totalRevenue: 150 },
      transaction,
    });
  });

  it('returns error for insufficient balance on withdrawal', async () => {
    const input: ProcessWalletTransactionInput = {
      leagueId: 'league-1',
      type: TransactionType.WITHDRAWAL,
      amount: 100,
      description: 'Test withdrawal',
    };

    const wallet = {
      id: 'wallet-1',
      leagueId: 'league-1',
      balance: 50,
      totalRevenue: 50,
      totalPlatformFees: 0,
      totalWithdrawn: 0,
      currency: 'USD',
      createdAt: new Date(),
    };

    walletRepository.findByLeagueId.mockResolvedValue(wallet);

    const result = await useCase.execute(input);

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