import { AppendTeamRatingEventsUseCase } from './AppendTeamRatingEventsUseCase';
import { ITeamRatingEventRepository } from '@core/racing/domain/repositories/ITeamRatingEventRepository';
import { ITeamRatingRepository } from '@core/racing/domain/repositories/ITeamRatingRepository';
import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';

// Mock repositories
class MockTeamRatingEventRepository implements ITeamRatingEventRepository {
  private events: TeamRatingEvent[] = [];

  async save(event: TeamRatingEvent): Promise<TeamRatingEvent> {
    this.events.push(event);
    return event;
  }

  async findByTeamId(teamId: string): Promise<TeamRatingEvent[]> {
    return this.events.filter(e => e.teamId === teamId);
  }

  async findByIds(ids: TeamRatingEventId[]): Promise<TeamRatingEvent[]> {
    return this.events.filter(e => ids.some(id => id.equals(e.id)));
  }

  async getAllByTeamId(teamId: string): Promise<TeamRatingEvent[]> {
    return this.events.filter(e => e.teamId === teamId);
  }

  async findEventsPaginated(teamId: string): Promise<any> {
    const events = await this.getAllByTeamId(teamId);
    return {
      items: events,
      total: events.length,
      limit: 10,
      offset: 0,
      hasMore: false,
    };
  }

  clear() {
    this.events = [];
  }
}

class MockTeamRatingRepository implements ITeamRatingRepository {
  private snapshots: Map<string, any> = new Map();

  async findByTeamId(teamId: string): Promise<any | null> {
    return this.snapshots.get(teamId) || null;
  }

  async save(snapshot: any): Promise<any> {
    this.snapshots.set(snapshot.teamId, snapshot);
    return snapshot;
  }

  clear() {
    this.snapshots.clear();
  }
}

describe('AppendTeamRatingEventsUseCase', () => {
  let useCase: AppendTeamRatingEventsUseCase;
  let mockEventRepo: MockTeamRatingEventRepository;
  let mockRatingRepo: MockTeamRatingRepository;

  beforeEach(() => {
    mockEventRepo = new MockTeamRatingEventRepository();
    mockRatingRepo = new MockTeamRatingRepository();
    useCase = new AppendTeamRatingEventsUseCase(mockEventRepo, mockRatingRepo);
  });

  afterEach(() => {
    mockEventRepo.clear();
    mockRatingRepo.clear();
  });

  describe('execute', () => {
    it('should do nothing when no events provided', async () => {
      await useCase.execute([]);
      
      const events = await mockEventRepo.getAllByTeamId('team-123');
      expect(events.length).toBe(0);
    });

    it('should save single event and update snapshot', async () => {
      const event = 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-456' },
        reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
        visibility: { public: true },
        version: 1,
      });

      await useCase.execute([event]);

      // Check event was saved
      const savedEvents = await mockEventRepo.getAllByTeamId('team-123');
      expect(savedEvents.length).toBe(1);
      expect(savedEvents[0]!.id.equals(event.id)).toBe(true);

      // Check snapshot was updated
      const snapshot = await mockRatingRepo.findByTeamId('team-123');
      expect(snapshot).toBeDefined();
      expect(snapshot.teamId).toBe('team-123');
      expect(snapshot.driving.value).toBe(60); // 50 + 10
    });

    it('should save multiple events for same team and update snapshot', async () => {
      const event1 = 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-456' },
        reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
        visibility: { public: true },
        version: 1,
      });

      const event2 = TeamRatingEvent.create({
        id: TeamRatingEventId.generate(),
        teamId: 'team-123',
        dimension: TeamRatingDimensionKey.create('driving'),
        delta: TeamRatingDelta.create(5),
        occurredAt: new Date('2024-01-01T11:00:00Z'),
        createdAt: new Date('2024-01-01T11:00:00Z'),
        source: { type: 'race', id: 'race-457' },
        reason: { code: 'RACE_FINISH', description: 'Finished 2nd' },
        visibility: { public: true },
        version: 1,
      });

      await useCase.execute([event1, event2]);

      // Check both events were saved
      const savedEvents = await mockEventRepo.getAllByTeamId('team-123');
      expect(savedEvents.length).toBe(2);

      // Check snapshot was updated with weighted average
      const snapshot = await mockRatingRepo.findByTeamId('team-123');
      expect(snapshot).toBeDefined();
      expect(snapshot.teamId).toBe('team-123');
      // Should be 50 + weighted average of (10, 5) = 50 + 7.5 = 57.5
      expect(snapshot.driving.value).toBe(57.5);
    });

    it('should handle multiple teams in one batch', async () => {
      const event1 = 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-456' },
        reason: { code: 'RACE_FINISH', description: 'Finished 1st' },
        visibility: { public: true },
        version: 1,
      });

      const event2 = TeamRatingEvent.create({
        id: TeamRatingEventId.generate(),
        teamId: 'team-456',
        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-789' },
        reason: { code: 'POSITIVE_ADMIN_ACTION', description: 'Helped organize event' },
        visibility: { public: true },
        version: 1,
      });

      await useCase.execute([event1, event2]);

      // Check both team snapshots were updated
      const snapshot1 = await mockRatingRepo.findByTeamId('team-123');
      const snapshot2 = await mockRatingRepo.findByTeamId('team-456');

      expect(snapshot1).toBeDefined();
      expect(snapshot1.driving.value).toBe(60);

      expect(snapshot2).toBeDefined();
      expect(snapshot2.adminTrust.value).toBe(55);
    });
  });
});