import { describe, it, expect, vi, Mocked } from 'vitest';
import { TeamService } from './TeamService';
import { TeamsApiClient } from '@/lib/api/teams/TeamsApiClient';
import { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel';
import { TeamDetailsViewModel } from '@/lib/view-models/TeamDetailsViewModel';
import { TeamMemberViewModel } from '@/lib/view-models/TeamMemberViewModel';

describe('TeamService', () => {
  let mockApiClient: Mocked<TeamsApiClient>;
  let service: TeamService;

  beforeEach(() => {
    mockApiClient = {
      getAll: vi.fn(),
      getDetails: vi.fn(),
      getMembers: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
      getDriverTeam: vi.fn(),
    } as Mocked<TeamsApiClient>;

    service = new TeamService(mockApiClient);
  });

  describe('getAllTeams', () => {
    it('should call apiClient.getAll and return array of TeamSummaryViewModel', async () => {
      const mockDto = {
        teams: [
          {
            id: 'team-1',
            name: 'Test Team',
            tag: 'TT',
            description: 'A test team',
            memberCount: 5,
            leagues: ['league-1'],
            specialization: 'endurance' as const,
            region: 'EU',
            languages: ['en'],
          },
        ],
        totalCount: 1,
      };

      mockApiClient.getAll.mockResolvedValue(mockDto);

      const result = await service.getAllTeams();

      expect(mockApiClient.getAll).toHaveBeenCalled();
      expect(result.isOk()).toBe(true);
      const teams = result.unwrap();
      expect(teams).toHaveLength(1);
      expect(teams[0].id).toBe('team-1');
      expect(teams[0].name).toBe('Test Team');
      expect(teams[0].tag).toBe('TT');
    });

    it('should throw error when apiClient.getAll fails', async () => {
      const error = new Error('API call failed');
      mockApiClient.getAll.mockRejectedValue(error);

      const result = await service.getAllTeams();
      expect(result.isErr()).toBe(true);
      expect(result.getError().message).toBe('API call failed');
    });
  });

  describe('getTeamDetails', () => {
    it('should call apiClient.getDetails and return TeamDetailsViewModel when data exists', async () => {
      const mockDto = {
        team: {
          id: 'team-1',
          name: 'Test Team',
          tag: 'TT',
          description: 'A test team',
          ownerId: 'owner-1',
          leagues: ['league-1'],
          createdAt: '2023-01-01T00:00:00Z',
          specialization: 'endurance',
          region: 'EU',
          languages: ['en'],
        },
        membership: {
          role: 'member',
          joinedAt: '2023-01-01T00:00:00Z',
          isActive: true,
        },
        canManage: false,
      };

      mockApiClient.getDetails.mockResolvedValue(mockDto);

      const result = await service.getTeamDetails('team-1', 'user-1');

      expect(mockApiClient.getDetails).toHaveBeenCalledWith('team-1');
      expect(result.isOk()).toBe(true);
      const details = result.unwrap();
      expect(details.team.id).toBe('team-1');
      expect(details.team.name).toBe('Test Team');
      expect(details.team.tag).toBe('TT');
    });

    it('should return null when apiClient.getDetails returns null', async () => {
      mockApiClient.getDetails.mockResolvedValue(null as any);

      const result = await service.getTeamDetails('team-1', 'user-1');

      expect(mockApiClient.getDetails).toHaveBeenCalledWith('team-1');
      expect(result.isErr()).toBe(true);
      expect(result.getError().type).toBe('notFound');
    });

    it('should throw error when apiClient.getDetails fails', async () => {
      const error = new Error('API call failed');
      mockApiClient.getDetails.mockRejectedValue(error);

      const result = await service.getTeamDetails('team-1', 'user-1');
      expect(result.isErr()).toBe(true);
      expect(result.getError().message).toBe('API call failed');
    });
  });

  describe('getTeamMembers', () => {
    it('should call apiClient.getMembers and return array of TeamMemberViewModel', async () => {
      const mockDto = {
        members: [
          {
            driverId: 'driver-1',
            driverName: 'Driver One',
            role: 'member' as const,
            joinedAt: '2023-01-01T00:00:00Z',
            isActive: true,
            avatarUrl: 'avatar.png',
          },
        ],
        totalCount: 1,
        ownerCount: 0,
        managerCount: 0,
        memberCount: 1,
      };

      mockApiClient.getMembers.mockResolvedValue(mockDto);

      const result = await service.getTeamMembers('team-1', 'user-1', 'owner-1');

      expect(mockApiClient.getMembers).toHaveBeenCalledWith('team-1');
      expect(result.isOk()).toBe(true);
      const members = result.unwrap();
      expect(members).toHaveLength(1);
      expect(members[0]).toBeInstanceOf(TeamMemberViewModel);
      expect(members[0].driverId).toBe('driver-1');
      expect(members[0].role).toBe('member');
    });

    it('should throw error when apiClient.getMembers fails', async () => {
      const error = new Error('API call failed');
      mockApiClient.getMembers.mockRejectedValue(error);

      const result = await service.getTeamMembers('team-1', 'user-1', 'owner-1');
      expect(result.isErr()).toBe(true);
      expect(result.getError().message).toBe('API call failed');
    });
  });

  describe('createTeam', () => {
    it('should call apiClient.create and return the result', async () => {
      const input = { name: 'New Team', tag: 'NT', description: 'A new team' };

      const mockOutput = { id: 'team-123', success: true };

      mockApiClient.create.mockResolvedValue(mockOutput);

      const result = await service.createTeam(input);

      expect(mockApiClient.create).toHaveBeenCalledWith(input);
      expect(result.isOk()).toBe(true);
      expect(result.unwrap()).toEqual(mockOutput);
    });

    it('should throw error when apiClient.create fails', async () => {
      const input = { name: 'New Team', tag: 'NT' };

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

      const result = await service.createTeam(input);
      expect(result.isErr()).toBe(true);
      expect(result.getError().message).toBe('API call failed');
    });
  });

  describe('updateTeam', () => {
    it('should call apiClient.update and return the result', async () => {
      const input = { name: 'Updated Team', description: 'Updated description' };

      const mockOutput = { success: true };

      mockApiClient.update.mockResolvedValue(mockOutput);

      const result = await service.updateTeam('team-1', input);

      expect(mockApiClient.update).toHaveBeenCalledWith('team-1', input);
      expect(result.isOk()).toBe(true);
      expect(result.unwrap()).toEqual(mockOutput);
    });

    it('should throw error when apiClient.update fails', async () => {
      const input = { name: 'Updated Team' };

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

      const result = await service.updateTeam('team-1', input);
      expect(result.isErr()).toBe(true);
      expect(result.getError().message).toBe('API call failed');
    });
  });

  describe('getDriverTeam', () => {
    it('should call apiClient.getDriverTeam and return the result', async () => {
      const mockOutput = {
        team: {
          id: 'team-1',
          name: 'Test Team',
          tag: 'TT',
          description: 'A test team',
          ownerId: 'owner-1',
          leagues: [],
          specialization: 'endurance' as const,
          region: 'EU',
          languages: ['en'],
        },
        membership: {
          role: 'member',
          joinedAt: '2023-01-01T00:00:00Z',
          isActive: true,
        },
        isOwner: false,
        canManage: false,
      };

      mockApiClient.getDriverTeam.mockResolvedValue(mockOutput);

      const result = await service.getDriverTeam('driver-1');

      expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith('driver-1');
      expect(result.isOk()).toBe(true);
      const data = result.unwrap();
      expect(data?.teamId).toBe('team-1');
      expect(data?.teamName).toBe('Test Team');
      expect(data?.role).toBe('member');
    });

    it('should return null when apiClient.getDriverTeam returns null', async () => {
      mockApiClient.getDriverTeam.mockResolvedValue(null);

      const result = await service.getDriverTeam('driver-1');

      expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith('driver-1');
      expect(result.isOk()).toBe(true);
      expect(result.unwrap()).toBeNull();
    });

    it('should throw error when apiClient.getDriverTeam fails', async () => {
      const error = new Error('API call failed');
      mockApiClient.getDriverTeam.mockRejectedValue(error);

      const result = await service.getDriverTeam('driver-1');
      expect(result.isErr()).toBe(true);
      expect(result.getError().message).toBe('API call failed');
    });
  });
});