import { TeamRatingFactoryUseCase } from './TeamRatingFactoryUseCase';
import type { ITeamRaceResultsProvider } from '../ports/ITeamRaceResultsProvider';
import type { Logger } from '@core/shared/application';
import { TeamDrivingRaceFactsDto } from '@core/racing/domain/services/TeamDrivingRatingEventFactory';

// Mock provider
class MockTeamRaceResultsProvider implements ITeamRaceResultsProvider {
  private results: TeamDrivingRaceFactsDto | null = null;

  async getTeamRaceResults(raceId: string): Promise<TeamDrivingRaceFactsDto | null> {
    return this.results;
  }

  setResults(results: TeamDrivingRaceFactsDto | null) {
    this.results = results;
  }
}

// Mock logger
const mockLogger: Logger = {
  info: () => {},
  error: () => {},
  warn: () => {},
  debug: () => {},
};

describe('TeamRatingFactoryUseCase', () => {
  let useCase: TeamRatingFactoryUseCase;
  let mockResultsProvider: MockTeamRaceResultsProvider;

  beforeEach(() => {
    mockResultsProvider = new MockTeamRaceResultsProvider();
    useCase = new TeamRatingFactoryUseCase(mockResultsProvider, mockLogger);
  });

  describe('execute', () => {
    it('should return error when race results not found', async () => {
      mockResultsProvider.setResults(null);

      const result = await useCase.execute({ raceId: 'race-123' });

      expect(result.success).toBe(false);
      expect(result.errors).toContain('Team race results not found');
      expect(result.events).toEqual([]);
    });

    it('should return success with no events when results are empty', async () => {
      mockResultsProvider.setResults({
        raceId: 'race-123',
        teamId: 'team-123',
        results: [],
      });

      const result = await useCase.execute({ raceId: 'race-123' });

      expect(result.success).toBe(true);
      expect(result.events).toEqual([]);
      expect(result.errors).toEqual([]);
    });

    it('should create events for single team', async () => {
      const raceResults: TeamDrivingRaceFactsDto = {
        raceId: 'race-123',
        teamId: 'team-123',
        results: [
          {
            teamId: 'team-123',
            position: 1,
            incidents: 0,
            status: 'finished',
            fieldSize: 3,
            strengthOfField: 55,
          },
        ],
      };

      mockResultsProvider.setResults(raceResults);

      const result = await useCase.execute({ raceId: 'race-123' });

      expect(result.success).toBe(true);
      expect(result.events.length).toBeGreaterThan(0);
      expect(result.errors).toEqual([]);

      // Verify events have correct structure
      const event = result.events[0];
      expect(event.teamId).toBe('team-123');
      expect(event.source.type).toBe('race');
      expect(event.source.id).toBe('race-123');
    });

    it('should create events for multiple teams', async () => {
      const raceResults: TeamDrivingRaceFactsDto = {
        raceId: 'race-123',
        teamId: 'team-123',
        results: [
          {
            teamId: 'team-123',
            position: 1,
            incidents: 0,
            status: 'finished',
            fieldSize: 3,
            strengthOfField: 55,
          },
          {
            teamId: 'team-456',
            position: 2,
            incidents: 1,
            status: 'finished',
            fieldSize: 3,
            strengthOfField: 55,
          },
        ],
      };

      mockResultsProvider.setResults(raceResults);

      const result = await useCase.execute({ raceId: 'race-123' });

      expect(result.success).toBe(true);
      expect(result.events.length).toBeGreaterThan(0);
      
      // Should have events for both teams
      const team123Events = result.events.filter(e => e.teamId === 'team-123');
      const team456Events = result.events.filter(e => e.teamId === 'team-456');
      
      expect(team123Events.length).toBeGreaterThan(0);
      expect(team456Events.length).toBeGreaterThan(0);
    });

    it('should handle optional ratings in results', async () => {
      const raceResults: TeamDrivingRaceFactsDto = {
        raceId: 'race-123',
        teamId: 'team-123',
        results: [
          {
            teamId: 'team-123',
            position: 1,
            incidents: 0,
            status: 'finished',
            fieldSize: 3,
            strengthOfField: 65,
            pace: 85,
            consistency: 80,
            teamwork: 90,
            sportsmanship: 95,
          },
        ],
      };

      mockResultsProvider.setResults(raceResults);

      const result = await useCase.execute({ raceId: 'race-123' });

      expect(result.success).toBe(true);
      expect(result.events.length).toBeGreaterThan(5); // Should have many events

      // Verify events include optional rating events
      const reasonCodes = result.events.map(e => e.reason.code);
      expect(reasonCodes).toContain('RACE_PACE');
      expect(reasonCodes).toContain('RACE_CONSISTENCY');
      expect(reasonCodes).toContain('RACE_TEAMWORK');
      expect(reasonCodes).toContain('RACE_SPORTSMANSHIP');
    });

    it('should handle repository errors', async () => {
      const raceResults: TeamDrivingRaceFactsDto = {
        raceId: 'race-123',
        teamId: 'team-123',
        results: [
          {
            teamId: 'team-123',
            position: 1,
            incidents: 0,
            status: 'finished',
            fieldSize: 1,
            strengthOfField: 55,
          },
        ],
      };

      mockResultsProvider.setResults(raceResults);

      // Mock provider to throw error
      mockResultsProvider.getTeamRaceResults = async () => {
        throw new Error('Provider error');
      };

      const result = await useCase.execute({ raceId: 'race-123' });

      expect(result.success).toBe(false);
      expect(result.errors.length).toBeGreaterThan(0);
      expect(result.errors[0]).toContain('Provider error');
      expect(result.events).toEqual([]);
    });

    it('should handle race with minimal events generated', async () => {
      const raceResults: TeamDrivingRaceFactsDto = {
        raceId: 'race-123',
        teamId: 'team-123',
        results: [
          {
            teamId: 'team-123',
            position: 1,
            incidents: 0,
            status: 'dns',
            fieldSize: 1,
            strengthOfField: 50,
          },
        ],
      };

      mockResultsProvider.setResults(raceResults);

      const result = await useCase.execute({ raceId: 'race-123' });

      expect(result.success).toBe(true);
      expect(result.events.length).toBeGreaterThan(0); // DNS creates penalty event
    });
  });

  describe('createManualEvents', () => {
    it('should create manual events with source ID', () => {
      const events = useCase.createManualEvents(
        'team-123',
        'driving',
        5,
        'MANUAL_ADJUSTMENT',
        'manualAdjustment',
        'adjustment-123'
      );

      expect(events).toHaveLength(1);
      expect(events[0].teamId).toBe('team-123');
      expect(events[0].dimension.value).toBe('driving');
      expect(events[0].delta.value).toBe(5);
      expect(events[0].reason.code).toBe('MANUAL_ADJUSTMENT');
      expect(events[0].source.type).toBe('manualAdjustment');
      expect(events[0].source.id).toBe('adjustment-123');
    });

    it('should create manual events without source ID', () => {
      const events = useCase.createManualEvents(
        'team-456',
        'adminTrust',
        -3,
        'PENALTY',
        'penalty'
      );

      expect(events).toHaveLength(1);
      expect(events[0].teamId).toBe('team-456');
      expect(events[0].dimension.value).toBe('adminTrust');
      expect(events[0].delta.value).toBe(-3);
      expect(events[0].reason.code).toBe('PENALTY');
      expect(events[0].source.type).toBe('penalty');
      expect(events[0].source.id).toBeUndefined();
    });
  });
});