/**
 * Integration Test: GetDriversLeaderboardUseCase Orchestration
 * 
 * Tests the orchestration logic of GetDriversLeaderboardUseCase:
 * - GetDriversLeaderboardUseCase: Retrieves list of drivers with rankings and statistics
 * - Validates that Use Cases correctly interact with their Ports (Repositories, other Use Cases)
 * - Uses In-Memory adapters for fast, deterministic testing
 * 
 * Focus: Business logic orchestration, NOT UI rendering
 */

import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import { InMemoryDriverRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryDriverRepository';
import { InMemoryDriverStatsRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryDriverStatsRepository';
import { GetDriversLeaderboardUseCase } from '../../../core/racing/application/use-cases/GetDriversLeaderboardUseCase';
import { RankingUseCase } from '../../../core/racing/application/use-cases/RankingUseCase';
import { DriverStatsUseCase } from '../../../core/racing/application/use-cases/DriverStatsUseCase';
import { Driver } from '../../../core/racing/domain/entities/Driver';
import { Logger } from '../../../core/shared/domain/Logger';

describe('GetDriversLeaderboardUseCase Orchestration', () => {
  let driverRepository: InMemoryDriverRepository;
  let driverStatsRepository: InMemoryDriverStatsRepository;
  let rankingUseCase: RankingUseCase;
  let driverStatsUseCase: DriverStatsUseCase;
  let getDriversLeaderboardUseCase: GetDriversLeaderboardUseCase;
  let mockLogger: Logger;

  beforeAll(() => {
    mockLogger = {
      info: () => {},
      debug: () => {},
      warn: () => {},
      error: () => {},
    } as unknown as Logger;
    
    driverRepository = new InMemoryDriverRepository(mockLogger);
    driverStatsRepository = new InMemoryDriverStatsRepository(mockLogger);
    
    // RankingUseCase and DriverStatsUseCase are dependencies of GetDriversLeaderboardUseCase
    rankingUseCase = new RankingUseCase(
      {} as any, // standingRepository not used in getAllDriverRankings
      {} as any, // driverRepository not used in getAllDriverRankings
      driverStatsRepository,
      mockLogger
    );
    
    driverStatsUseCase = new DriverStatsUseCase(
      {} as any, // resultRepository not used in getDriverStats
      {} as any, // standingRepository not used in getDriverStats
      driverStatsRepository,
      mockLogger
    );

    getDriversLeaderboardUseCase = new GetDriversLeaderboardUseCase(
      driverRepository,
      rankingUseCase,
      driverStatsUseCase,
      mockLogger
    );
  });

  beforeEach(() => {
    driverRepository.clear();
    driverStatsRepository.clear();
  });

  describe('GetDriversLeaderboardUseCase - Success Path', () => {
    it('should retrieve complete list of drivers with all data', async () => {
      // Scenario: System has multiple drivers
      // Given: 3 drivers exist with various data
      const drivers = [
        Driver.create({ id: 'd1', iracingId: '1', name: 'Driver 1', country: 'US' }),
        Driver.create({ id: 'd2', iracingId: '2', name: 'Driver 2', country: 'UK' }),
        Driver.create({ id: 'd3', iracingId: '3', name: 'Driver 3', country: 'DE' }),
      ];
      
      for (const d of drivers) {
        await driverRepository.create(d);
      }

      // And: Each driver has statistics
      await driverStatsRepository.saveDriverStats('d1', {
        rating: 2000,
        totalRaces: 10,
        wins: 2,
        podiums: 5,
        overallRank: 1,
        safetyRating: 4.5,
        sportsmanshipRating: 95,
        dnfs: 0,
        avgFinish: 3.5,
        bestFinish: 1,
        worstFinish: 10,
        consistency: 85,
        experienceLevel: 'pro'
      });
      await driverStatsRepository.saveDriverStats('d2', {
        rating: 1800,
        totalRaces: 8,
        wins: 1,
        podiums: 3,
        overallRank: 2,
        safetyRating: 4.0,
        sportsmanshipRating: 90,
        dnfs: 1,
        avgFinish: 5.2,
        bestFinish: 1,
        worstFinish: 15,
        consistency: 75,
        experienceLevel: 'intermediate'
      });
      await driverStatsRepository.saveDriverStats('d3', {
        rating: 1500,
        totalRaces: 5,
        wins: 0,
        podiums: 1,
        overallRank: 3,
        safetyRating: 3.5,
        sportsmanshipRating: 80,
        dnfs: 0,
        avgFinish: 8.0,
        bestFinish: 3,
        worstFinish: 12,
        consistency: 65,
        experienceLevel: 'rookie'
      });

      // When: GetDriversLeaderboardUseCase.execute() is called
      const result = await getDriversLeaderboardUseCase.execute({});

      // Then: The result should contain all drivers
      expect(result.isOk()).toBe(true);
      const leaderboard = result.unwrap();
      
      expect(leaderboard.items).toHaveLength(3);
      expect(leaderboard.totalRaces).toBe(23);
      expect(leaderboard.totalWins).toBe(3);
      expect(leaderboard.activeCount).toBe(3);

      // And: Drivers should be sorted by rating (high to low)
      expect(leaderboard.items[0].driver.id).toBe('d1');
      expect(leaderboard.items[1].driver.id).toBe('d2');
      expect(leaderboard.items[2].driver.id).toBe('d3');
      
      expect(leaderboard.items[0].rating).toBe(2000);
      expect(leaderboard.items[1].rating).toBe(1800);
      expect(leaderboard.items[2].rating).toBe(1500);
    });

    it('should handle empty drivers list', async () => {
      // Scenario: System has no registered drivers
      // Given: No drivers exist in the system
      // When: GetDriversLeaderboardUseCase.execute() is called
      const result = await getDriversLeaderboardUseCase.execute({});

      // Then: The result should contain an empty array
      expect(result.isOk()).toBe(true);
      const leaderboard = result.unwrap();
      expect(leaderboard.items).toHaveLength(0);
      expect(leaderboard.totalRaces).toBe(0);
      expect(leaderboard.totalWins).toBe(0);
      expect(leaderboard.activeCount).toBe(0);
    });

    it('should correctly identify active drivers', async () => {
      // Scenario: Some drivers have no races
      // Given: 2 drivers exist, one with races, one without
      await driverRepository.create(Driver.create({ id: 'active', iracingId: '1', name: 'Active', country: 'US' }));
      await driverRepository.create(Driver.create({ id: 'inactive', iracingId: '2', name: 'Inactive', country: 'UK' }));
      
      await driverStatsRepository.saveDriverStats('active', {
        rating: 1500,
        totalRaces: 1,
        wins: 0,
        podiums: 0,
        overallRank: 1,
        safetyRating: 3.0,
        sportsmanshipRating: 70,
        dnfs: 0,
        avgFinish: 10,
        bestFinish: 10,
        worstFinish: 10,
        consistency: 50,
        experienceLevel: 'rookie'
      });
      // No stats for inactive driver or totalRaces = 0
      await driverStatsRepository.saveDriverStats('inactive', {
        rating: 1000,
        totalRaces: 0,
        wins: 0,
        podiums: 0,
        overallRank: null,
        safetyRating: 2.5,
        sportsmanshipRating: 50,
        dnfs: 0,
        avgFinish: 0,
        bestFinish: 0,
        worstFinish: 0,
        consistency: 0,
        experienceLevel: 'rookie'
      });

      // When: GetDriversLeaderboardUseCase.execute() is called
      const result = await getDriversLeaderboardUseCase.execute({});

      // Then: Only one driver should be active
      const leaderboard = result.unwrap();
      expect(leaderboard.activeCount).toBe(1);
      expect(leaderboard.items.find(i => i.driver.id === 'active')?.isActive).toBe(true);
      expect(leaderboard.items.find(i => i.driver.id === 'inactive')?.isActive).toBe(false);
    });
  });

  describe('GetDriversLeaderboardUseCase - Error Handling', () => {
    it('should handle repository errors gracefully', async () => {
      // Scenario: Repository throws error
      // Given: DriverRepository throws an error during query
      const originalFindAll = driverRepository.findAll.bind(driverRepository);
      driverRepository.findAll = async () => {
        throw new Error('Repository error');
      };

      // When: GetDriversLeaderboardUseCase.execute() is called
      const result = await getDriversLeaderboardUseCase.execute({});

      // Then: Should return a repository error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('REPOSITORY_ERROR');

      // Restore original method
      driverRepository.findAll = originalFindAll;
    });
  });
});
