import { vi, describe, it, expect, beforeEach } from 'vitest';
import {
  InMemoryGameRepository,
  InMemorySeasonRepository,
  InMemoryLeagueScoringConfigRepository,
  InMemoryChampionshipStandingRepository,
} from './InMemoryScoringRepositories';
import { Game } from '@core/racing/domain/entities/Game';
import { Season } from '@core/racing/domain/entities/season/Season';
import { LeagueScoringConfig } from '@core/racing/domain/entities/LeagueScoringConfig';
import { ChampionshipStanding } from '@core/racing/domain/entities/championship/ChampionshipStanding';
import type { Logger } from '@core/shared/application';

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

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

  describe('InMemoryGameRepository', () => {
    let repository: InMemoryGameRepository;

    beforeEach(() => {
      repository = new InMemoryGameRepository(mockLogger);
    });

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

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

      it('should return the game if found', async () => {
        const game = Game.create({ id: 'test-game', name: 'Test Game' });
        repository.seed(game);
        const result = await repository.findById('test-game');
        expect(result).toEqual(game);
        expect(mockLogger.info).toHaveBeenCalledWith(`Found game: ${game.id}`);
      });
    });

    describe('findAll', () => {
      it('should return all games', async () => {
        const game1 = Game.create({ id: 'game1', name: 'Game 1' });
        const game2 = Game.create({ id: 'game2', name: 'Game 2' });
        repository.seed(game1);
        repository.seed(game2);
        const result = await repository.findAll();
        expect(result).toEqual([game1, game2]);
        expect(mockLogger.info).toHaveBeenCalledWith('Found 2 games.');
      });
    });
  });

  describe('InMemorySeasonRepository', () => {
    let repository: InMemorySeasonRepository;

    beforeEach(() => {
      repository = new InMemorySeasonRepository(mockLogger);
    });

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

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

      it('should return the season if found', async () => {
        const season = Season.create({
          id: 'test-season',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'Test Season',
          year: 2025,
          order: 1,
          status: 'active',
          startDate: new Date(),
          endDate: new Date(),
        });
        repository.seed(season);
        const result = await repository.findById('test-season');
        expect(result).toEqual(season);
      });
    });

    describe('findByLeagueId', () => {
      it('should return seasons for the league', async () => {
        const season1 = Season.create({
          id: 'season1',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'Season 1',
          year: 2025,
          order: 1,
          status: 'active',
          startDate: new Date(),
          endDate: new Date(),
        });
        const season2 = Season.create({
          id: 'season2',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'Season 2',
          year: 2025,
          order: 2,
          status: 'active',
          startDate: new Date(),
          endDate: new Date(),
        });
        repository.seed(season1);
        repository.seed(season2);
        const result = await repository.findByLeagueId('league1');
        expect(result).toEqual([season1, season2]);
      });
    });

    describe('create', () => {
      it('should create a season', async () => {
        const season = Season.create({
          id: 'new-season',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'New Season',
          year: 2025,
          order: 1,
          status: 'active',
          startDate: new Date(),
          endDate: new Date(),
        });
        const result = await repository.create(season);
        expect(result).toEqual(season);
        const found = await repository.findById('new-season');
        expect(found).toEqual(season);
      });
    });

    describe('add', () => {
      it('should add a season', async () => {
        const season = Season.create({
          id: 'add-season',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'Add Season',
          year: 2025,
          order: 1,
          status: 'active',
          startDate: new Date(),
          endDate: new Date(),
        });
        await repository.add(season);
        const found = await repository.findById('add-season');
        expect(found).toEqual(season);
      });
    });

    describe('update', () => {
      it('should update an existing season', async () => {
        const season = Season.create({
          id: 'update-season',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'Update Season',
          year: 2025,
          order: 1,
          status: 'active',
          startDate: new Date(),
          endDate: new Date(),
        });
        repository.seed(season);
        const updatedSeason = { ...season, name: 'Updated Name' } as Season;
        await repository.update(updatedSeason);
        const found = await repository.findById('update-season');
        expect(found?.name).toBe('Updated Name');
      });
    });

    describe('listByLeague', () => {
      it('should list seasons by league', async () => {
        const season = Season.create({
          id: 'list-season',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'List Season',
          year: 2025,
          order: 1,
          status: 'active',
          startDate: new Date(),
          endDate: new Date(),
        });
        repository.seed(season);
        const result = await repository.listByLeague('league1');
        expect(result).toEqual([season]);
      });
    });

    describe('listActiveByLeague', () => {
      it('should list active seasons by league', async () => {
        const activeSeason = Season.create({
          id: 'active-season',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'Active Season',
          year: 2025,
          order: 1,
          status: 'active',
          startDate: new Date(),
          endDate: new Date(),
        });
        const inactiveSeason = Season.create({
          id: 'inactive-season',
          leagueId: 'league1',
          gameId: 'game1',
          name: 'Inactive Season',
          year: 2025,
          order: 2,
          status: 'completed',
          startDate: new Date(),
          endDate: new Date(),
        });
        repository.seed(activeSeason);
        repository.seed(inactiveSeason);
        const result = await repository.listActiveByLeague('league1');
        expect(result).toEqual([activeSeason]);
      });
    });
  });

  describe('InMemoryLeagueScoringConfigRepository', () => {
    let repository: InMemoryLeagueScoringConfigRepository;

    beforeEach(() => {
      repository = new InMemoryLeagueScoringConfigRepository(mockLogger);
    });

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

    describe('findBySeasonId', () => {
      it('should return null if config not found', async () => {
        const result = await repository.findBySeasonId('nonexistent');
        expect(result).toBeNull();
        expect(mockLogger.warn).toHaveBeenCalledWith('League scoring config for seasonId nonexistent not found.');
      });

      it('should return the config if found', async () => {
        const config = {
          id: 'config1',
          seasonId: 'season1',
          scoringPresetId: 'preset1',
          championships: [],
          equals: vi.fn(),
        } as unknown as LeagueScoringConfig;
        repository.seed(config);
        const result = await repository.findBySeasonId('season1');
        expect(result).toEqual(config);
      });
    });

    describe('save', () => {
      it('should save a new config', async () => {
        const config = {
          id: 'new-config',
          seasonId: 'season1',
          scoringPresetId: 'preset1',
          championships: [],
          equals: vi.fn(),
        } as unknown as LeagueScoringConfig;
        const result = await repository.save(config);
        expect(result).toEqual(config);
        const found = await repository.findBySeasonId('season1');
        expect(found).toEqual(config);
      });

      it('should update an existing config', async () => {
        const config = {
          id: 'update-config',
          seasonId: 'season1',
          scoringPresetId: 'preset1',
          championships: [],
          equals: vi.fn(),
        } as unknown as LeagueScoringConfig;
        repository.seed(config);
        const updatedConfig = { ...config, scoringPresetId: 'preset2' } as unknown as LeagueScoringConfig;
        const result = await repository.save(updatedConfig);
        expect(result).toEqual(updatedConfig);
        const found = await repository.findBySeasonId('season1');
        expect(found?.scoringPresetId).toBe('preset2');
      });
    });
  });

  describe('InMemoryChampionshipStandingRepository', () => {
    let repository: InMemoryChampionshipStandingRepository;

    beforeEach(() => {
      repository = new InMemoryChampionshipStandingRepository(mockLogger);
    });

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

    describe('findBySeasonAndChampionship', () => {
      it('should return standings for season and championship', async () => {
        const standing = ChampionshipStanding.create({
          seasonId: 'season1',
          championshipId: 'champ1',
          participant: { type: 'driver', id: 'driver1' },
          totalPoints: 100,
          resultsCounted: 10,
          resultsDropped: 0,
          position: 1,
        });
        repository.seed(standing);
        const result = await repository.findBySeasonAndChampionship('season1', 'champ1');
        expect(result).toEqual([standing]);
      });
    });

    describe('saveAll', () => {
      it('should save all standings', async () => {
        const standing1 = ChampionshipStanding.create({
          seasonId: 'season1',
          championshipId: 'champ1',
          participant: { type: 'driver', id: 'driver1' },
          totalPoints: 100,
          resultsCounted: 10,
          resultsDropped: 0,
          position: 1,
        });
        const standing2 = ChampionshipStanding.create({
          seasonId: 'season1',
          championshipId: 'champ1',
          participant: { type: 'driver', id: 'driver2' },
          totalPoints: 80,
          resultsCounted: 10,
          resultsDropped: 0,
          position: 2,
        });
        await repository.saveAll([standing1, standing2]);
        const result = await repository.findBySeasonAndChampionship('season1', 'champ1');
        expect(result).toEqual([standing1, standing2]);
      });
    });

    describe('getAll', () => {
      it('should return all standings', () => {
        const standing = ChampionshipStanding.create({
          seasonId: 'season1',
          championshipId: 'champ1',
          participant: { type: 'driver', id: 'driver1' },
          totalPoints: 100,
          resultsCounted: 10,
          resultsDropped: 0,
          position: 1,
        });
        repository.seed(standing);
        const result = repository.getAll();
        expect(result).toEqual([standing]);
      });
    });
  });
});