/**
 * Tests for GetUserRatingsSummaryQuery
 */

import { GetUserRatingsSummaryQuery, GetUserRatingsSummaryQueryHandler } from './GetUserRatingsSummaryQuery';
import { UserRating } from '../../domain/value-objects/UserRating';
import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile';
import { GameKey } from '../../domain/value-objects/GameKey';
import { ExternalRating } from '../../domain/value-objects/ExternalRating';
import { ExternalRatingProvenance } from '../../domain/value-objects/ExternalRatingProvenance';
import { RatingEvent } from '../../domain/entities/RatingEvent';
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
import { RatingDelta } from '../../domain/value-objects/RatingDelta';

describe('GetUserRatingsSummaryQuery', () => {
  let mockUserRatingRepo: any;
  let mockExternalRatingRepo: any;
  let mockRatingEventRepo: any;
  let handler: GetUserRatingsSummaryQueryHandler;

  beforeEach(() => {
    mockUserRatingRepo = {
      findByUserId: jest.fn(),
    };
    mockExternalRatingRepo = {
      findByUserId: jest.fn(),
    };
    mockRatingEventRepo = {
      getAllByUserId: jest.fn(),
    };

    handler = new GetUserRatingsSummaryQueryHandler(
      mockUserRatingRepo,
      mockExternalRatingRepo,
      mockRatingEventRepo
    );
  });

  describe('execute', () => {
    it('should return summary with platform and external ratings', async () => {
      const userId = 'user-123';

      // Mock user rating
      const userRating = UserRating.create(userId);
      mockUserRatingRepo.findByUserId.mockResolvedValue(userRating);

      // Mock external ratings
      const gameKey = GameKey.create('iracing');
      const profile = ExternalGameRatingProfile.create({
        userId: { toString: () => userId } as any,
        gameKey,
        ratings: new Map([
          ['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
          ['safetyRating', ExternalRating.create(gameKey, 'safetyRating', 4.5)],
        ]),
        provenance: ExternalRatingProvenance.create('iRacing API', new Date()),
      });
      mockExternalRatingRepo.findByUserId.mockResolvedValue([profile]);

      // Mock rating events
      const event = RatingEvent.create({
        id: RatingEventId.create(),
        userId,
        dimension: RatingDimensionKey.create('driver'),
        delta: RatingDelta.create(5),
        occurredAt: new Date('2024-01-01'),
        createdAt: new Date('2024-01-01'),
        source: { type: 'race', id: 'race-123' },
        reason: { code: 'RACE_FINISH', summary: 'Good race', details: {} },
        visibility: { public: true, redactedFields: [] },
        version: 1,
      });
      mockRatingEventRepo.getAllByUserId.mockResolvedValue([event]);

      const query: GetUserRatingsSummaryQuery = { userId };
      const result = await handler.execute(query);

      expect(result.userId).toBe(userId);
      expect(result.platform.driving.value).toBe(50); // Default
      expect(result.platform.overallReputation).toBe(50);
      expect(result.external.iracing.iRating).toBe(2200);
      expect(result.external.iracing.safetyRating).toBe(4.5);
      expect(result.lastRatingEventAt).toBe('2024-01-01T00:00:00.000Z');
    });

    it('should handle missing user rating gracefully', async () => {
      const userId = 'user-123';

      mockUserRatingRepo.findByUserId.mockResolvedValue(null);
      mockExternalRatingRepo.findByUserId.mockResolvedValue([]);
      mockRatingEventRepo.getAllByUserId.mockResolvedValue([]);

      const query: GetUserRatingsSummaryQuery = { userId };
      const result = await handler.execute(query);

      expect(result.userId).toBe(userId);
      expect(result.platform.driving.value).toBe(0);
      expect(result.platform.overallReputation).toBe(0);
      expect(result.external).toEqual({});
      expect(result.lastRatingEventAt).toBeUndefined();
    });

    it('should handle multiple external game profiles', async () => {
      const userId = 'user-123';

      mockUserRatingRepo.findByUserId.mockResolvedValue(UserRating.create(userId));

      // Multiple game profiles
      const iracingProfile = ExternalGameRatingProfile.create({
        userId: { toString: () => userId } as any,
        gameKey: GameKey.create('iracing'),
        ratings: new Map([
          ['iRating', ExternalRating.create(GameKey.create('iracing'), 'iRating', 2200)],
        ]),
        provenance: ExternalRatingProvenance.create('iRacing API', new Date()),
      });

      const assettoProfile = ExternalGameRatingProfile.create({
        userId: { toString: () => userId } as any,
        gameKey: GameKey.create('assetto'),
        ratings: new Map([
          ['rating', ExternalRating.create(GameKey.create('assetto'), 'rating', 85)],
        ]),
        provenance: ExternalRatingProvenance.create('Assetto API', new Date()),
      });

      mockExternalRatingRepo.findByUserId.mockResolvedValue([iracingProfile, assettoProfile]);
      mockRatingEventRepo.getAllByUserId.mockResolvedValue([]);

      const query: GetUserRatingsSummaryQuery = { userId };
      const result = await handler.execute(query);

      expect(result.external.iracing.iRating).toBe(2200);
      expect(result.external.assetto.rating).toBe(85);
    });

    it('should handle empty external ratings', async () => {
      const userId = 'user-123';

      mockUserRatingRepo.findByUserId.mockResolvedValue(UserRating.create(userId));
      mockExternalRatingRepo.findByUserId.mockResolvedValue([]);
      mockRatingEventRepo.getAllByUserId.mockResolvedValue([]);

      const query: GetUserRatingsSummaryQuery = { userId };
      const result = await handler.execute(query);

      expect(result.external).toEqual({});
    });

    it('should use current date for timestamps when no user rating exists', async () => {
      const userId = 'user-123';

      mockUserRatingRepo.findByUserId.mockResolvedValue(null);
      mockExternalRatingRepo.findByUserId.mockResolvedValue([]);
      mockRatingEventRepo.getAllByUserId.mockResolvedValue([]);

      const beforeQuery = new Date();
      const query: GetUserRatingsSummaryQuery = { userId };
      const result = await handler.execute(query);
      const afterQuery = new Date();

      // Should have valid ISO date strings
      expect(new Date(result.createdAt).getTime()).toBeGreaterThanOrEqual(beforeQuery.getTime());
      expect(new Date(result.createdAt).getTime()).toBeLessThanOrEqual(afterQuery.getTime());
      expect(new Date(result.updatedAt).getTime()).toBeGreaterThanOrEqual(beforeQuery.getTime());
      expect(new Date(result.updatedAt).getTime()).toBeLessThanOrEqual(afterQuery.getTime());
    });
  });
});
