import { RatingEventFactory, RaceFactsDto } from './RatingEventFactory';

describe('RatingEventFactory', () => {
  describe('createFromRaceFinish', () => {
    it('should create events from race finish data', () => {
      const events = RatingEventFactory.createFromRaceFinish({
        userId: 'user-123',
        raceId: 'race-456',
        position: 3,
        totalDrivers: 10,
        startPosition: 5,
        incidents: 1,
        fieldStrength: 2500,
        status: 'finished',
      });

      expect(events.length).toBeGreaterThan(0);
      const event = events[0];
      expect(event).toBeDefined();
      if (event) {
        expect(event.userId).toBe('user-123');
        expect(event.source.type).toBe('race');
        expect(event.source.id).toBe('race-456');
      }
    });

    it('should create events for DNS status', () => {
      const events = RatingEventFactory.createFromRaceFinish({
        userId: 'user-123',
        raceId: 'race-456',
        position: 10,
        totalDrivers: 10,
        startPosition: 5,
        incidents: 0,
        fieldStrength: 2500,
        status: 'dns',
      });

      expect(events.length).toBeGreaterThan(0);
      const dnsEvent = events.find(e => e.reason.code.includes('DNS'));
      expect(dnsEvent).toBeDefined();
    });

    it('should create events for DNF status', () => {
      const events = RatingEventFactory.createFromRaceFinish({
        userId: 'user-123',
        raceId: 'race-456',
        position: 10,
        totalDrivers: 10,
        startPosition: 5,
        incidents: 2,
        fieldStrength: 2500,
        status: 'dnf',
      });

      expect(events.length).toBeGreaterThan(0);
      const dnfEvent = events.find(e => e.reason.code.includes('DNF'));
      expect(dnfEvent).toBeDefined();
    });

    it('should create events for DSQ status', () => {
      const events = RatingEventFactory.createFromRaceFinish({
        userId: 'user-123',
        raceId: 'race-456',
        position: 10,
        totalDrivers: 10,
        startPosition: 5,
        incidents: 5,
        fieldStrength: 2500,
        status: 'dsq',
      });

      expect(events.length).toBeGreaterThan(0);
      const dsqEvent = events.find(e => e.reason.code.includes('DSQ'));
      expect(dsqEvent).toBeDefined();
    });

    it('should create events for AFK status', () => {
      const events = RatingEventFactory.createFromRaceFinish({
        userId: 'user-123',
        raceId: 'race-456',
        position: 10,
        totalDrivers: 10,
        startPosition: 5,
        incidents: 0,
        fieldStrength: 2500,
        status: 'afk',
      });

      expect(events.length).toBeGreaterThan(0);
      const afkEvent = events.find(e => e.reason.code.includes('AFK'));
      expect(afkEvent).toBeDefined();
    });
  });

  describe('createDrivingEventsFromRace', () => {
    it('should create events for single driver with good performance', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 5,
            finishPos: 2,
            incidents: 0,
            status: 'finished',
            sof: 2500,
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      
      expect(eventsByUser.has('user-123')).toBe(true);
      const events = eventsByUser.get('user-123')!;
      expect(events.length).toBeGreaterThan(0);
      
      const performanceEvent = events.find(e => e.reason.code === 'DRIVING_FINISH_STRENGTH_GAIN');
      expect(performanceEvent).toBeDefined();
      expect(performanceEvent?.delta.value).toBeGreaterThan(0);
    });

    it('should create events for multiple drivers', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 5,
            finishPos: 2,
            incidents: 0,
            status: 'finished',
            sof: 2500,
          },
          {
            userId: 'user-456',
            startPos: 3,
            finishPos: 8,
            incidents: 2,
            status: 'finished',
            sof: 2500,
          },
          {
            userId: 'user-789',
            startPos: 5,
            finishPos: 5,
            incidents: 0,
            status: 'dns',
            sof: 2500,
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      
      expect(eventsByUser.has('user-123')).toBe(true);
      expect(eventsByUser.has('user-456')).toBe(true);
      expect(eventsByUser.has('user-789')).toBe(true);
      
      // user-123 should have performance events
      const user123Events = eventsByUser.get('user-123')!;
      expect(user123Events.some(e => e.reason.code === 'DRIVING_FINISH_STRENGTH_GAIN')).toBe(true);
      
      // user-456 should have incident penalty
      const user456Events = eventsByUser.get('user-456')!;
      expect(user456Events.some(e => e.reason.code === 'DRIVING_INCIDENTS_PENALTY')).toBe(true);
      
      // user-789 should have DNS penalty
      const user789Events = eventsByUser.get('user-789')!;
      expect(user789Events.some(e => e.reason.code === 'DRIVING_DNS_PENALTY')).toBe(true);
    });

    it('should create positions gained bonus', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 10,
            finishPos: 3,
            incidents: 0,
            status: 'finished',
            sof: 2500,
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      const events = eventsByUser.get('user-123')!;
      
      const gainEvent = events.find(e => e.reason.code === 'DRIVING_POSITIONS_GAINED_BONUS');
      expect(gainEvent).toBeDefined();
      expect(gainEvent?.delta.value).toBeGreaterThan(0);
    });

    it('should handle DNF status', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 5,
            finishPos: 10,
            incidents: 0,
            status: 'dnf',
            sof: 2500,
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      const events = eventsByUser.get('user-123')!;
      
      const dnfEvent = events.find(e => e.reason.code === 'DRIVING_DNF_PENALTY');
      expect(dnfEvent).toBeDefined();
      expect(dnfEvent?.delta.value).toBe(-10);
    });

    it('should handle DSQ status', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 5,
            finishPos: 10,
            incidents: 0,
            status: 'dsq',
            sof: 2500,
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      const events = eventsByUser.get('user-123')!;
      
      const dsqEvent = events.find(e => e.reason.code === 'DRIVING_DSQ_PENALTY');
      expect(dsqEvent).toBeDefined();
      expect(dsqEvent?.delta.value).toBe(-25);
    });

    it('should handle AFK status', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 5,
            finishPos: 10,
            incidents: 0,
            status: 'afk',
            sof: 2500,
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      const events = eventsByUser.get('user-123')!;
      
      const afkEvent = events.find(e => e.reason.code === 'DRIVING_AFK_PENALTY');
      expect(afkEvent).toBeDefined();
      expect(afkEvent?.delta.value).toBe(-20);
    });

    it('should handle incident penalties', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 5,
            finishPos: 5,
            incidents: 3,
            status: 'finished',
            sof: 2500,
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      const events = eventsByUser.get('user-123')!;
      
      const incidentEvent = events.find(e => e.reason.code === 'DRIVING_INCIDENTS_PENALTY');
      expect(incidentEvent).toBeDefined();
      expect(incidentEvent?.delta.value).toBeLessThan(0);
    });

    it('should calculate SoF if not provided', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 5,
            finishPos: 2,
            incidents: 0,
            status: 'finished',
            // No sof
          },
          {
            userId: 'user-456',
            startPos: 3,
            finishPos: 8,
            incidents: 0,
            status: 'finished',
            // No sof
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      
      // Should still work without errors
      expect(eventsByUser.size).toBe(2);
      expect(eventsByUser.get('user-123')!.length).toBeGreaterThan(0);
    });

    it('should handle empty results', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      expect(eventsByUser.size).toBe(0);
    });

    it('should handle mixed statuses', () => {
      const raceFacts: RaceFactsDto = {
        raceId: 'race-123',
        results: [
          {
            userId: 'user-123',
            startPos: 5,
            finishPos: 2,
            incidents: 1,
            status: 'finished',
            sof: 2500,
          },
          {
            userId: 'user-456',
            startPos: 3,
            finishPos: 10,
            incidents: 0,
            status: 'dnf',
            sof: 2500,
          },
          {
            userId: 'user-789',
            startPos: 5,
            finishPos: 5,
            incidents: 0,
            status: 'dns',
            sof: 2500,
          },
        ],
      };

      const eventsByUser = RatingEventFactory.createDrivingEventsFromRace(raceFacts);
      
      expect(eventsByUser.size).toBe(3);
      
      // user-123: performance + incidents
      const user123Events = eventsByUser.get('user-123')!;
      expect(user123Events.length).toBeGreaterThanOrEqual(2);
      
      // user-456: DNF
      const user456Events = eventsByUser.get('user-456')!;
      expect(user456Events.some(e => e.reason.code === 'DRIVING_DNF_PENALTY')).toBe(true);
      
      // user-789: DNS
      const user789Events = eventsByUser.get('user-789')!;
      expect(user789Events.some(e => e.reason.code === 'DRIVING_DNS_PENALTY')).toBe(true);
    });
  });

  describe('createFromPenalty', () => {
    it('should create driving penalty event', () => {
      const events = RatingEventFactory.createFromPenalty({
        userId: 'user-123',
        penaltyId: 'penalty-789',
        penaltyType: 'incident',
        severity: 'major',
        reason: 'Caused collision',
      });

      expect(events.length).toBeGreaterThan(0);
      const event = events[0];
      expect(event).toBeDefined();
      if (event) {
        expect(event.dimension.value).toBe('driving');
        expect(event.source.type).toBe('penalty');
        expect(event.source.id).toBe('penalty-789');
      }
    });

    it('should create admin trust penalty event', () => {
      const events = RatingEventFactory.createFromPenalty({
        userId: 'user-123',
        penaltyId: 'penalty-789',
        penaltyType: 'admin_violation',
        severity: 'major',
        reason: 'Abuse of power',
      });

      expect(events.length).toBeGreaterThan(0);
      const event = events[0];
      expect(event).toBeDefined();
      if (event) {
        expect(event.dimension.value).toBe('adminTrust');
      }
    });
  });

  describe('createFromVote', () => {
    it('should create positive vote event', () => {
      const events = RatingEventFactory.createFromVote({
        userId: 'user-123',
        voteSessionId: 'vote-101',
        outcome: 'positive',
        voteCount: 8,
        eligibleVoterCount: 10,
        percentPositive: 80,
      });

      expect(events.length).toBeGreaterThan(0);
      const event = events[0];
      expect(event).toBeDefined();
      if (event) {
        expect(event.dimension.value).toBe('adminTrust');
        expect(event.delta.value).toBeGreaterThan(0);
      }
    });

    it('should create negative vote event', () => {
      const events = RatingEventFactory.createFromVote({
        userId: 'user-123',
        voteSessionId: 'vote-101',
        outcome: 'negative',
        voteCount: 2,
        eligibleVoterCount: 10,
        percentPositive: 20,
      });

      expect(events.length).toBeGreaterThan(0);
      const event = events[0];
      expect(event).toBeDefined();
      if (event) {
        expect(event.delta.value).toBeLessThan(0);
      }
    });
  });

  describe('createFromAdminAction', () => {
    it('should create admin action bonus event', () => {
      const events = RatingEventFactory.createFromAdminAction({
        userId: 'user-123',
        adminActionId: 'admin-202',
        actionType: 'sla_response',
        details: { responseTime: 30 },
      });

      expect(events.length).toBeGreaterThan(0);
      const event = events[0];
      expect(event).toBeDefined();
      if (event) {
        expect(event.dimension.value).toBe('adminTrust');
        expect(event.delta.value).toBeGreaterThan(0);
      }
    });

    it('should create admin action penalty event', () => {
      const events = RatingEventFactory.createFromAdminAction({
        userId: 'user-123',
        adminActionId: 'admin-202',
        actionType: 'abuse_report',
        details: { validated: true },
      });

      expect(events.length).toBeGreaterThan(0);
      const event = events[0];
      expect(event).toBeDefined();
      if (event) {
        expect(event.dimension.value).toBe('adminTrust');
        expect(event.delta.value).toBeLessThan(0);
      }
    });
  });
});