import { describe, it, expect, vi, Mocked } from 'vitest';
import { PaymentService } from './PaymentService';
import { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
import { PaymentViewModel, MembershipFeeViewModel, PrizeViewModel, WalletViewModel } from '../../view-models';

describe('PaymentService', () => {
  let mockApiClient: Mocked<PaymentsApiClient>;
  let service: PaymentService;

  beforeEach(() => {
    mockApiClient = {
      getPayments: vi.fn(),
      createPayment: vi.fn(),
      getMembershipFees: vi.fn(),
      getPrizes: vi.fn(),
      getWallet: vi.fn(),
    } as Mocked<PaymentsApiClient>;

    service = new PaymentService(mockApiClient);
  });

  describe('getPayments', () => {
    it('should call apiClient.getPayments and return PaymentViewModel array', async () => {
      const mockResponse = {
        payments: [
          {
            id: 'payment-1',
            type: 'sponsorship' as const,
            amount: 100,
            platformFee: 10,
            netAmount: 90,
            payerId: 'user-1',
            payerType: 'sponsor' as const,
            leagueId: 'league-1',
            status: 'completed' as const,
            createdAt: new Date(),
          },
        ],
      };

      mockApiClient.getPayments.mockResolvedValue(mockResponse);

      const result = await service.getPayments();

      expect(mockApiClient.getPayments).toHaveBeenCalledWith(undefined);
      expect(result).toHaveLength(1);
      expect(result[0]).toBeInstanceOf(PaymentViewModel);
      expect(result[0].id).toBe('payment-1');
    });

    it('should call apiClient.getPayments with filters', async () => {
      const mockResponse = { payments: [] };
      mockApiClient.getPayments.mockResolvedValue(mockResponse);

      await service.getPayments('league-1', 'user-1');

      expect(mockApiClient.getPayments).toHaveBeenCalledWith({ leagueId: 'league-1', payerId: 'user-1' });
    });
  });

  describe('getPayment', () => {
    it('should return PaymentViewModel when payment exists', async () => {
      const mockResponse = {
        payments: [
          {
            id: 'payment-1',
            type: 'sponsorship' as const,
            amount: 100,
            platformFee: 10,
            netAmount: 90,
            payerId: 'user-1',
            payerType: 'sponsor' as const,
            leagueId: 'league-1',
            status: 'completed' as const,
            createdAt: new Date(),
          },
        ],
      };

      mockApiClient.getPayments.mockResolvedValue(mockResponse);

      const result = await service.getPayment('payment-1');

      expect(result).toBeInstanceOf(PaymentViewModel);
      expect(result.id).toBe('payment-1');
    });

    it('should throw error when payment does not exist', async () => {
      const mockResponse = { payments: [] };
      mockApiClient.getPayments.mockResolvedValue(mockResponse);

      await expect(service.getPayment('non-existent')).rejects.toThrow(
        'Payment with ID non-existent not found'
      );
    });
  });

  describe('createPayment', () => {
    it('should call apiClient.createPayment and return PaymentViewModel', async () => {
      const input = {
        type: 'sponsorship' as const,
        amount: 100,
        payerId: 'user-1',
        payerType: 'sponsor' as const,
        leagueId: 'league-1',
      };

      const mockResponse = {
        payment: {
          id: 'payment-1',
          type: 'sponsorship' as const,
          amount: 100,
          platformFee: 10,
          netAmount: 90,
          payerId: 'user-1',
          payerType: 'sponsor' as const,
          leagueId: 'league-1',
          status: 'pending' as const,
          createdAt: new Date(),
        },
      };

      mockApiClient.createPayment.mockResolvedValue(mockResponse);

      const result = await service.createPayment(input);

      expect(mockApiClient.createPayment).toHaveBeenCalledWith(input);
      expect(result).toBeInstanceOf(PaymentViewModel);
      expect(result.id).toBe('payment-1');
    });
  });

  describe('getMembershipFees', () => {
    it('should return MembershipFeeViewModel when fee exists', async () => {
      const mockResponse = {
        fee: {
          id: 'fee-1',
          leagueId: 'league-1',
          type: 'season' as const,
          amount: 50,
          enabled: true,
          createdAt: new Date(),
          updatedAt: new Date(),
        },
        payments: [],
      };

      mockApiClient.getMembershipFees.mockResolvedValue(mockResponse);

      const result = await service.getMembershipFees('league-1');

      expect(result).toBeInstanceOf(MembershipFeeViewModel);
      expect(result!.id).toBe('fee-1');
    });

    it('should return null when fee does not exist', async () => {
      const mockResponse = {
        fee: null,
        payments: [],
      };

      mockApiClient.getMembershipFees.mockResolvedValue(mockResponse);

      const result = await service.getMembershipFees('league-1');

      expect(result).toBeNull();
    });
  });

  describe('getPrizes', () => {
    it('should call apiClient.getPrizes and return PrizeViewModel array', async () => {
      const mockResponse = {
        prizes: [
          {
            id: 'prize-1',
            leagueId: 'league-1',
            seasonId: 'season-1',
            position: 1,
            name: 'First Place',
            amount: 100,
            type: 'cash' as const,
            awarded: false,
            createdAt: new Date(),
          },
        ],
      };

      mockApiClient.getPrizes.mockResolvedValue(mockResponse);

      const result = await service.getPrizes();

      expect(mockApiClient.getPrizes).toHaveBeenCalledWith(undefined);
      expect(result).toHaveLength(1);
      expect(result[0]).toBeInstanceOf(PrizeViewModel);
      expect(result[0].id).toBe('prize-1');
    });

    it('should call apiClient.getPrizes with filters', async () => {
      const mockResponse = { prizes: [] };
      mockApiClient.getPrizes.mockResolvedValue(mockResponse);

      await service.getPrizes('league-1', 'season-1');

      expect(mockApiClient.getPrizes).toHaveBeenCalledWith({ leagueId: 'league-1', seasonId: 'season-1' });
    });
  });

  describe('getWallet', () => {
    it('should call apiClient.getWallet and return WalletViewModel', async () => {
      const mockResponse = {
        wallet: {
          id: 'wallet-1',
          leagueId: 'league-1',
          balance: 1000,
          totalRevenue: 1500,
          totalPlatformFees: 100,
          totalWithdrawn: 400,
          createdAt: new Date(),
          currency: 'EUR',
        },
        transactions: [
          {
            id: 'tx-1',
            walletId: 'wallet-1',
            type: 'deposit' as const,
            amount: 500,
            description: 'Deposit',
            createdAt: new Date(),
          },
        ],
      };

      mockApiClient.getWallet.mockResolvedValue(mockResponse);

      const result = await service.getWallet('league-1');

      expect(mockApiClient.getWallet).toHaveBeenCalledWith({ leagueId: 'league-1' });
      expect(result).toBeInstanceOf(WalletViewModel);
      expect(result.id).toBe('wallet-1');
      expect(result.balance).toBe(1000);
      expect(result.transactions).toHaveLength(1);
    });
  });
});