import { describe, it, expect, vi, Mocked } from 'vitest';
import { LeagueMembershipService } from './LeagueMembershipService';
import { LeaguesApiClient } from '../../api/leagues/LeaguesApiClient';
import { LeagueMemberViewModel } from '../../view-models/LeagueMemberViewModel';

describe('LeagueMembershipService', () => {
  let mockApiClient: Mocked<LeaguesApiClient>;
  let service: LeagueMembershipService;

  beforeEach(() => {
    mockApiClient = {
      getMemberships: vi.fn(),
      removeMember: vi.fn(),
    } as unknown as Mocked<LeaguesApiClient>;

    service = new LeagueMembershipService(mockApiClient);
  });

  describe('getLeagueMemberships', () => {
    it('should call apiClient.getMemberships and return array of LeagueMemberViewModel', async () => {
      const leagueId = 'league-123';
      const currentUserId = 'user-456';

      const mockDto = {
        members: [
          { driverId: 'driver-1' },
          { driverId: 'driver-2' },
        ],
      } as any;

      mockApiClient.getMemberships.mockResolvedValue(mockDto);

      const result = await service.getLeagueMemberships(leagueId, currentUserId);

      expect(mockApiClient.getMemberships).toHaveBeenCalledWith(leagueId);
      expect(result).toHaveLength(2);

      const first = result[0]!;
      const second = result[1]!;

      expect(first).toBeInstanceOf(LeagueMemberViewModel);
      expect(first.driverId).toBe('driver-1');
      expect(first.currentUserId).toBe(currentUserId);

      expect(second).toBeInstanceOf(LeagueMemberViewModel);
      expect(second.driverId).toBe('driver-2');
      expect(second.currentUserId).toBe(currentUserId);
    });

    it('should handle empty members array', async () => {
      const leagueId = 'league-123';
      const currentUserId = 'user-456';

      const mockDto = { members: [] } as any;

      mockApiClient.getMemberships.mockResolvedValue(mockDto);

      const result = await service.getLeagueMemberships(leagueId, currentUserId);

      expect(result).toHaveLength(0);
    });

    it('should throw error when apiClient.getMemberships fails', async () => {
      const leagueId = 'league-123';
      const currentUserId = 'user-456';

      const error = new Error('API call failed');
      mockApiClient.getMemberships.mockRejectedValue(error);

      await expect(service.getLeagueMemberships(leagueId, currentUserId)).rejects.toThrow('API call failed');
    });
  });

  describe('removeMember', () => {
    it('should call apiClient.removeMember and return the result', async () => {
      const leagueId = 'league-123';
      const performerDriverId = 'performer-456';
      const targetDriverId = 'target-789';

      const mockResult = { success: true };
      mockApiClient.removeMember.mockResolvedValue(mockResult);

      const result = await service.removeMember(leagueId, performerDriverId, targetDriverId);

      expect(mockApiClient.removeMember).toHaveBeenCalledWith(leagueId, performerDriverId, targetDriverId);
      expect(result).toEqual(mockResult);
    });

    it('should handle unsuccessful removal', async () => {
      const leagueId = 'league-123';
      const performerDriverId = 'performer-456';
      const targetDriverId = 'target-789';

      const mockResult = { success: false };
      mockApiClient.removeMember.mockResolvedValue(mockResult);

      const result = await service.removeMember(leagueId, performerDriverId, targetDriverId);

      expect(result.success).toBe(false);
    });

    it('should throw error when apiClient.removeMember fails', async () => {
      const leagueId = 'league-123';
      const performerDriverId = 'performer-456';
      const targetDriverId = 'target-789';

      const error = new Error('API call failed');
      mockApiClient.removeMember.mockRejectedValue(error);

      await expect(service.removeMember(leagueId, performerDriverId, targetDriverId)).rejects.toThrow('API call failed');
    });
  });
});