import { vi, describe, it, expect, beforeEach } from 'vitest';
import { InMemoryTeamRepository } from './InMemoryTeamRepository';
import type { Logger } from '@core/shared/application';
import { Team } from '@core/racing/domain/entities/Team';

describe('InMemoryTeamRepository', () => {
  let repository: InMemoryTeamRepository;
  let mockLogger: Logger;

  beforeEach(() => {
    mockLogger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    };
    repository = new InMemoryTeamRepository(mockLogger);
  });

  describe('constructor', () => {
    it('should initialize with a logger', () => {
      expect(repository).toBeDefined();
      expect(mockLogger.info).toHaveBeenCalledWith('InMemoryTeamRepository initialized.');
    });
  });

  describe('findById', () => {
    it('should return null if team not found', async () => {
      const result = await repository.findById('nonexistent');
      expect(result).toBeNull();
      expect(mockLogger.debug).toHaveBeenCalledWith('Finding team by id: nonexistent');
      expect(mockLogger.warn).toHaveBeenCalledWith('Team with id nonexistent not found.');
    });

    it('should return the team if found', async () => {
      const team = Team.create({
        id: 'team1',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'owner1',
        leagues: ['league1'],
      });
      await repository.create(team);

      const result = await repository.findById('team1');
      expect(result).toEqual(team);
      expect(mockLogger.debug).toHaveBeenCalledWith('Finding team by id: team1');
      expect(mockLogger.info).toHaveBeenCalledWith('Found team: team1.');
    });
  });

  describe('findAll', () => {
    it('should return an empty array when no teams', async () => {
      const result = await repository.findAll();
      expect(result).toEqual([]);
      expect(mockLogger.debug).toHaveBeenCalledWith('Finding all teams.');
      expect(mockLogger.info).toHaveBeenCalledWith('Found 0 teams.');
    });

    it('should return all teams', async () => {
      const team1 = Team.create({
        id: 'team1',
        name: 'Test Team 1',
        tag: 'TT1',
        description: 'A test team 1',
        ownerId: 'owner1',
        leagues: ['league1'],
      });
      const team2 = Team.create({
        id: 'team2',
        name: 'Test Team 2',
        tag: 'TT2',
        description: 'A test team 2',
        ownerId: 'owner2',
        leagues: ['league2'],
      });
      await repository.create(team1);
      await repository.create(team2);

      const result = await repository.findAll();
      expect(result).toEqual([team1, team2]);
      expect(mockLogger.debug).toHaveBeenCalledWith('Finding all teams.');
      expect(mockLogger.info).toHaveBeenCalledWith('Found 2 teams.');
    });
  });

  describe('findByLeagueId', () => {
    it('should return empty array if no teams in league', async () => {
      const result = await repository.findByLeagueId('league1');
      expect(result).toEqual([]);
      expect(mockLogger.debug).toHaveBeenCalledWith('Finding teams by league id: league1');
      expect(mockLogger.info).toHaveBeenCalledWith('Found 0 teams for league id: league1.');
    });

    it('should return teams in the league', async () => {
      const team1 = Team.create({
        id: 'team1',
        name: 'Test Team 1',
        tag: 'TT1',
        description: 'A test team 1',
        ownerId: 'owner1',
        leagues: ['league1'],
      });
      const team2 = Team.create({
        id: 'team2',
        name: 'Test Team 2',
        tag: 'TT2',
        description: 'A test team 2',
        ownerId: 'owner2',
        leagues: ['league1', 'league2'],
      });
      await repository.create(team1);
      await repository.create(team2);

      const result = await repository.findByLeagueId('league1');
      expect(result).toEqual([team1, team2]);
      expect(mockLogger.debug).toHaveBeenCalledWith('Finding teams by league id: league1');
      expect(mockLogger.info).toHaveBeenCalledWith('Found 2 teams for league id: league1.');
    });
  });

  describe('create', () => {
    it('should create a new team', async () => {
      const team = Team.create({
        id: 'team1',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'owner1',
        leagues: ['league1'],
      });

      const result = await repository.create(team);
      expect(result).toEqual(team);
      expect(mockLogger.debug).toHaveBeenCalledWith('Creating team: team1');
      expect(mockLogger.info).toHaveBeenCalledWith('Team team1 created successfully.');
    });

    it('should throw error if team already exists', async () => {
      const team = Team.create({
        id: 'team1',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'owner1',
        leagues: ['league1'],
      });
      await repository.create(team);

      await expect(repository.create(team)).rejects.toThrow('Team with ID team1 already exists');
      expect(mockLogger.warn).toHaveBeenCalledWith('Team with ID team1 already exists.');
    });
  });

  describe('update', () => {
    it('should update an existing team', async () => {
      const team = Team.create({
        id: 'team1',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'owner1',
        leagues: ['league1'],
      });
      await repository.create(team);

      const updatedTeam = team.update({ name: 'Updated Team' });
      const result = await repository.update(updatedTeam);
      expect(result).toEqual(updatedTeam);
      expect(mockLogger.debug).toHaveBeenCalledWith('Updating team: team1');
      expect(mockLogger.info).toHaveBeenCalledWith('Team team1 updated successfully.');
    });

    it('should throw error if team does not exist', async () => {
      const team = Team.create({
        id: 'team1',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'owner1',
        leagues: ['league1'],
      });

      await expect(repository.update(team)).rejects.toThrow('Team with ID team1 not found');
      expect(mockLogger.warn).toHaveBeenCalledWith('Team with ID team1 not found for update.');
    });
  });

  describe('delete', () => {
    it('should delete an existing team', async () => {
      const team = Team.create({
        id: 'team1',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'owner1',
        leagues: ['league1'],
      });
      await repository.create(team);

      await repository.delete('team1');
      expect(await repository.exists('team1')).toBe(false);
      expect(mockLogger.debug).toHaveBeenCalledWith('Deleting team: team1');
      expect(mockLogger.info).toHaveBeenCalledWith('Team team1 deleted successfully.');
    });

    it('should throw error if team does not exist', async () => {
      await expect(repository.delete('nonexistent')).rejects.toThrow('Team with ID nonexistent not found');
      expect(mockLogger.warn).toHaveBeenCalledWith('Team with ID nonexistent not found for deletion.');
    });
  });

  describe('exists', () => {
    it('should return true if team exists', async () => {
      const team = Team.create({
        id: 'team1',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'owner1',
        leagues: ['league1'],
      });
      await repository.create(team);

      const result = await repository.exists('team1');
      expect(result).toBe(true);
      expect(mockLogger.debug).toHaveBeenCalledWith('Checking existence of team with id: team1');
    });

    it('should return false if team does not exist', async () => {
      const result = await repository.exists('nonexistent');
      expect(result).toBe(false);
      expect(mockLogger.debug).toHaveBeenCalledWith('Checking existence of team with id: nonexistent');
    });
  });
});