/**
 * Integration Test: League Detail Use Case Orchestration
 *
 * Tests the orchestration logic of league detail-related Use Cases:
 * - GetLeagueUseCase: Retrieves league details
 * - Validates that Use Cases correctly interact with their Ports (Repositories, Event Publishers)
 * - Uses In-Memory adapters for fast, deterministic testing
 *
 * Focus: Business logic orchestration, NOT UI rendering
 */

import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import { InMemoryLeagueRepository } from '../../../adapters/leagues/persistence/inmemory/InMemoryLeagueRepository';
import { InMemoryLeagueEventPublisher } from '../../../adapters/leagues/events/InMemoryLeagueEventPublisher';
import { GetLeagueUseCase } from '../../../core/leagues/application/use-cases/GetLeagueUseCase';
import { CreateLeagueUseCase } from '../../../core/leagues/application/use-cases/CreateLeagueUseCase';
import { LeagueCreateCommand } from '../../../core/leagues/application/ports/LeagueCreateCommand';

describe('League Detail Use Case Orchestration', () => {
  let leagueRepository: InMemoryLeagueRepository;
  let eventPublisher: InMemoryLeagueEventPublisher;
  let getLeagueUseCase: GetLeagueUseCase;
  let createLeagueUseCase: CreateLeagueUseCase;

  beforeAll(() => {
    leagueRepository = new InMemoryLeagueRepository();
    eventPublisher = new InMemoryLeagueEventPublisher();
    getLeagueUseCase = new GetLeagueUseCase(leagueRepository, eventPublisher);
    createLeagueUseCase = new CreateLeagueUseCase(leagueRepository, eventPublisher);
  });

  beforeEach(() => {
    leagueRepository.clear();
    eventPublisher.clear();
  });

  describe('GetLeagueDetailUseCase - Success Path', () => {
    it('should retrieve complete league detail with all data', async () => {
      // Scenario: League with complete data
      // Given: A league exists with complete data
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Complete League',
        description: 'A league with all data',
        visibility: 'public',
        ownerId: driverId,
        maxDrivers: 20,
        approvalRequired: true,
        lateJoinAllowed: true,
        raceFrequency: 'weekly',
        raceDay: 'Saturday',
        raceTime: '18:00',
        tracks: ['Monza', 'Spa'],
        scoringSystem: { points: [25, 18, 15] },
        bonusPointsEnabled: true,
        penaltiesEnabled: true,
        protestsEnabled: true,
        appealsEnabled: true,
        stewardTeam: ['steward-1'],
        gameType: 'iRacing',
        skillLevel: 'Intermediate',
        category: 'GT3',
        tags: ['competitive'],
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain all league sections
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('Complete League');
      expect(result.description).toBe('A league with all data');
      expect(result.ownerId).toBe(driverId);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
      const events = eventPublisher.getLeagueAccessedEvents();
      expect(events[0].leagueId).toBe(league.id);
      expect(events[0].driverId).toBe(driverId);
    });

    it('should retrieve league detail with minimal data', async () => {
      // Scenario: League with minimal data
      // Given: A league exists with only basic information (name, description, owner)
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Minimal League',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain basic league info
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('Minimal League');
      expect(result.ownerId).toBe(driverId);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });

    it('should retrieve league detail with career history but no recent results', async () => {
      // Scenario: League with career history but no recent results
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Career History League',
        description: 'A league with career history',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain career history
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });

    it('should retrieve league detail with recent results but no career history', async () => {
      // Scenario: League with recent results but no career history
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Recent Results League',
        description: 'A league with recent results',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain recent race results
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });

    it('should retrieve league detail with championship standings but no other data', async () => {
      // Scenario: League with championship standings but no other data
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Championship League',
        description: 'A league with championship standings',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain championship standings
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });

    it('should retrieve league detail with social links but no team affiliation', async () => {
      // Scenario: League with social links but no team affiliation
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Social Links League',
        description: 'A league with social links',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain social links
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });

    it('should retrieve league detail with team affiliation but no social links', async () => {
      // Scenario: League with team affiliation but no social links
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Team Affiliation League',
        description: 'A league with team affiliation',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain team affiliation
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });
  });

  describe('GetLeagueDetailUseCase - Edge Cases', () => {
    it('should handle league with no career history', async () => {
      // Scenario: League with no career history
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'No Career History League',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain league profile
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });

    it('should handle league with no recent race results', async () => {
      // Scenario: League with no recent race results
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'No Recent Results League',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain league profile
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });

    it('should handle league with no championship standings', async () => {
      // Scenario: League with no championship standings
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'No Championship League',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain league profile
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });

    it('should handle league with no data at all', async () => {
      // Scenario: League with absolutely no data
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'No Data League',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called with league ID
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: The result should contain basic league info
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('No Data League');

      // And: EventPublisher should emit LeagueAccessedEvent
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(1);
    });
  });

  describe('GetLeagueDetailUseCase - Error Handling', () => {
    it('should throw error when league does not exist', async () => {
      // Scenario: Non-existent league
      // Given: No league exists with the given ID
      const nonExistentLeagueId = 'non-existent-league-id';
      
      // When: GetLeagueUseCase.execute() is called with non-existent league ID
      // Then: Should throw error
      await expect(getLeagueUseCase.execute({ leagueId: nonExistentLeagueId, driverId: 'driver-123' }))
        .rejects.toThrow();

      // And: EventPublisher should NOT emit any events
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(0);
    });

    it('should throw error when league ID is invalid', async () => {
      // Scenario: Invalid league ID
      // Given: An invalid league ID (e.g., empty string)
      const invalidLeagueId = '';
      
      // When: GetLeagueUseCase.execute() is called with invalid league ID
      // Then: Should throw error
      await expect(getLeagueUseCase.execute({ leagueId: invalidLeagueId, driverId: 'driver-123' }))
        .rejects.toThrow();

      // And: EventPublisher should NOT emit any events
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(0);
    });

    it('should handle repository errors gracefully', async () => {
      // Scenario: Repository throws error
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Test League',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // And: LeagueRepository throws an error during query
      const originalFindById = leagueRepository.findById;
      leagueRepository.findById = async () => {
        throw new Error('Repository error');
      };

      // When: GetLeagueUseCase.execute() is called
      // Then: Should propagate the error appropriately
      await expect(getLeagueUseCase.execute({ leagueId: league.id, driverId }))
        .rejects.toThrow('Repository error');

      // And: EventPublisher should NOT emit any events
      expect(eventPublisher.getLeagueAccessedEventCount()).toBe(0);

      // Restore original method
      leagueRepository.findById = originalFindById;
    });
  });

  describe('League Detail Data Orchestration', () => {
    it('should correctly calculate league statistics from race results', async () => {
      // Scenario: League statistics calculation
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Statistics League',
        description: 'A league for statistics calculation',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: League statistics should show:
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('Statistics League');
    });

    it('should correctly format career history with league and team information', async () => {
      // Scenario: Career history formatting
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Career History League',
        description: 'A league for career history formatting',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: Career history should show:
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('Career History League');
    });

    it('should correctly format recent race results with proper details', async () => {
      // Scenario: Recent race results formatting
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Recent Results League',
        description: 'A league for recent results formatting',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: Recent race results should show:
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('Recent Results League');
    });

    it('should correctly aggregate championship standings across leagues', async () => {
      // Scenario: Championship standings aggregation
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Championship League',
        description: 'A league for championship standings',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: Championship standings should show:
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('Championship League');
    });

    it('should correctly format social links with proper URLs', async () => {
      // Scenario: Social links formatting
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Social Links League',
        description: 'A league for social links formatting',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: Social links should show:
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('Social Links League');
    });

    it('should correctly format team affiliation with role', async () => {
      // Scenario: Team affiliation formatting
      // Given: A league exists
      const driverId = 'driver-123';
      const league = await createLeagueUseCase.execute({
        name: 'Team Affiliation League',
        description: 'A league for team affiliation formatting',
        visibility: 'public',
        ownerId: driverId,
        approvalRequired: false,
        lateJoinAllowed: false,
        bonusPointsEnabled: false,
        penaltiesEnabled: false,
        protestsEnabled: false,
        appealsEnabled: false,
      });

      // When: GetLeagueUseCase.execute() is called
      const result = await getLeagueUseCase.execute({ leagueId: league.id, driverId });

      // Then: Team affiliation should show:
      expect(result).toBeDefined();
      expect(result.id).toBe(league.id);
      expect(result.name).toBe('Team Affiliation League');
    });
  });
  
});
