import { TeamRatingSnapshotCalculator } from './TeamRatingSnapshotCalculator';
import { TeamRatingEvent } from '../entities/TeamRatingEvent';
import { TeamRatingEventId } from '../value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '../value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '../value-objects/TeamRatingDelta';
import { TeamRatingValue } from '../value-objects/TeamRatingValue';

describe('TeamRatingSnapshotCalculator', () => {
  describe('calculate', () => {
    it('should return default ratings for empty events', () => {
      const snapshot = TeamRatingSnapshotCalculator.calculate('team-123', []);

      expect(snapshot.teamId).toBe('team-123');
      expect(snapshot.driving.value).toBe(50);
      expect(snapshot.adminTrust.value).toBe(50);
      expect(snapshot.overall).toBe(50);
      expect(snapshot.eventCount).toBe(0);
    });

    it('should calculate single dimension rating', () => {
      const events = [
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(10),
          occurredAt: new Date('2024-01-01T10:00:00Z'),
          createdAt: new Date('2024-01-01T10:00:00Z'),
          source: { type: 'race', id: 'race-1' },
          reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
          visibility: { public: true },
          version: 1,
        }),
      ];

      const snapshot = TeamRatingSnapshotCalculator.calculate('team-123', events);

      expect(snapshot.driving.value).toBe(60); // 50 + 10
      expect(snapshot.adminTrust.value).toBe(50); // Default
      expect(snapshot.overall).toBeCloseTo(57, 1); // 60 * 0.7 + 50 * 0.3 = 57
    });

    it('should calculate multiple events with weights', () => {
      const events = [
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(10),
          weight: 1,
          occurredAt: new Date('2024-01-01T10:00:00Z'),
          createdAt: new Date('2024-01-01T10:00:00Z'),
          source: { type: 'race', id: 'race-1' },
          reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
          visibility: { public: true },
          version: 1,
        }),
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(-5),
          weight: 2,
          occurredAt: new Date('2024-01-01T11:00:00Z'),
          createdAt: new Date('2024-01-01T11:00:00Z'),
          source: { type: 'race', id: 'race-2' },
          reason: { code: 'RACE_PENALTY', description: 'Incident penalty' },
          visibility: { public: true },
          version: 1,
        }),
      ];

      const snapshot = TeamRatingSnapshotCalculator.calculate('team-123', events);

      // Weighted average: (10*1 + (-5)*2) / (1+2) = 0/3 = 0
      // So driving = 50 + 0 = 50
      expect(snapshot.driving.value).toBe(50);
    });

    it('should calculate mixed dimensions', () => {
      const events = [
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(15),
          occurredAt: new Date('2024-01-01T10:00:00Z'),
          createdAt: new Date('2024-01-01T10:00:00Z'),
          source: { type: 'race', id: 'race-1' },
          reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
          visibility: { public: true },
          version: 1,
        }),
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('adminTrust'),
          delta: TeamRatingDelta.create(5),
          occurredAt: new Date('2024-01-01T10:00:00Z'),
          createdAt: new Date('2024-01-01T10:00:00Z'),
          source: { type: 'adminAction', id: 'action-1' },
          reason: { code: 'ADMIN_BONUS', description: 'Helpful admin work' },
          visibility: { public: true },
          version: 1,
        }),
      ];

      const snapshot = TeamRatingSnapshotCalculator.calculate('team-123', events);

      expect(snapshot.driving.value).toBe(65); // 50 + 15
      expect(snapshot.adminTrust.value).toBe(55); // 50 + 5
      expect(snapshot.overall).toBeCloseTo(62, 1); // 65 * 0.7 + 55 * 0.3 = 62
    });

    it('should clamp values between 0 and 100', () => {
      const events = [
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(60), // Would make it 110
          occurredAt: new Date('2024-01-01T10:00:00Z'),
          createdAt: new Date('2024-01-01T10:00:00Z'),
          source: { type: 'race', id: 'race-1' },
          reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
          visibility: { public: true },
          version: 1,
        }),
      ];

      const snapshot = TeamRatingSnapshotCalculator.calculate('team-123', events);

      expect(snapshot.driving.value).toBe(100); // Clamped
    });

    it('should track last updated date', () => {
      const events = [
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(5),
          occurredAt: new Date('2024-01-01T10:00:00Z'),
          createdAt: new Date('2024-01-01T10:00:00Z'),
          source: { type: 'race', id: 'race-1' },
          reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
          visibility: { public: true },
          version: 1,
        }),
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(3),
          occurredAt: new Date('2024-01-02T10:00:00Z'),
          createdAt: new Date('2024-01-02T10:00:00Z'),
          source: { type: 'race', id: 'race-2' },
          reason: { code: 'RACE_FINISH', description: 'Finished 2nd' },
          visibility: { public: true },
          version: 1,
        }),
      ];

      const snapshot = TeamRatingSnapshotCalculator.calculate('team-123', events);

      expect(snapshot.lastUpdated).toEqual(new Date('2024-01-02T10:00:00Z'));
      expect(snapshot.eventCount).toBe(2);
    });
  });

  describe('calculateDimensionChange', () => {
    it('should calculate net change for a dimension', () => {
      const events = [
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(10),
          weight: 1,
          occurredAt: new Date('2024-01-01T10:00:00Z'),
          createdAt: new Date('2024-01-01T10:00:00Z'),
          source: { type: 'race', id: 'race-1' },
          reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
          visibility: { public: true },
          version: 1,
        }),
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(-5),
          weight: 2,
          occurredAt: new Date('2024-01-01T11:00:00Z'),
          createdAt: new Date('2024-01-01T11:00:00Z'),
          source: { type: 'race', id: 'race-2' },
          reason: { code: 'RACE_PENALTY', description: 'Incident penalty' },
          visibility: { public: true },
          version: 1,
        }),
      ];

      const change = TeamRatingSnapshotCalculator.calculateDimensionChange(
        TeamRatingDimensionKey.create('driving'),
        events
      );

      // (10*1 + (-5)*2) / (1+2) = 0/3 = 0
      expect(change).toBe(0);
    });

    it('should return 0 for no events', () => {
      const change = TeamRatingSnapshotCalculator.calculateDimensionChange(
        TeamRatingDimensionKey.create('driving'),
        []
      );

      expect(change).toBe(0);
    });
  });

  describe('calculateOverWindow', () => {
    it('should calculate ratings for a time window', () => {
      const allEvents = [
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(10),
          occurredAt: new Date('2024-01-01T10:00:00Z'),
          createdAt: new Date('2024-01-01T10:00:00Z'),
          source: { type: 'race', id: 'race-1' },
          reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
          visibility: { public: true },
          version: 1,
        }),
        TeamRatingEvent.create({
          id: TeamRatingEventId.generate(),
          teamId: 'team-123',
          dimension: TeamRatingDimensionKey.create('driving'),
          delta: TeamRatingDelta.create(5),
          occurredAt: new Date('2024-01-02T10:00:00Z'),
          createdAt: new Date('2024-01-02T10:00:00Z'),
          source: { type: 'race', id: 'race-2' },
          reason: { code: 'RACE_FINISH', description: 'Finished 2nd' },
          visibility: { public: true },
          version: 1,
        }),
      ];

      const snapshot = TeamRatingSnapshotCalculator.calculateOverWindow(
        'team-123',
        allEvents,
        new Date('2024-01-01T00:00:00Z'),
        new Date('2024-01-01T23:59:59Z')
      );

      // Only first event in window
      expect(snapshot.driving.value).toBe(60); // 50 + 10
      expect(snapshot.eventCount).toBe(1);
    });
  });

  describe('calculateDelta', () => {
    it('should calculate differences between snapshots', () => {
      const before = {
        teamId: 'team-123',
        driving: TeamRatingValue.create(50),
        adminTrust: TeamRatingValue.create(50),
        overall: 50,
        lastUpdated: new Date('2024-01-01'),
        eventCount: 10,
      };
  
      const after = {
        teamId: 'team-123',
        driving: TeamRatingValue.create(65),
        adminTrust: TeamRatingValue.create(55),
        overall: 62,
        lastUpdated: new Date('2024-01-02'),
        eventCount: 15,
      };

      const delta = TeamRatingSnapshotCalculator.calculateDelta(before, after);

      expect(delta.driving).toBe(15);
      expect(delta.adminTrust).toBe(5);
      expect(delta.overall).toBe(12);
    });
  });
});