/**
 * Tests for GetLeagueEligibilityPreviewQuery
 */

import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile';
import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository';
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
import { ExternalRating } from '../../domain/value-objects/ExternalRating';
import { ExternalRatingProvenance } from '../../domain/value-objects/ExternalRatingProvenance';
import { GameKey } from '../../domain/value-objects/GameKey';
import { UserId } from '../../domain/value-objects/UserId';
import { UserRating } from '../../domain/value-objects/UserRating';
import { GetLeagueEligibilityPreviewQuery, GetLeagueEligibilityPreviewQueryHandler } from './GetLeagueEligibilityPreviewQuery';

describe('GetLeagueEligibilityPreviewQuery', () => {
  let mockUserRatingRepo: UserRatingRepository;
  let mockExternalRatingRepo: ExternalGameRatingRepository;
  let handler: GetLeagueEligibilityPreviewQueryHandler;

  beforeEach(() => {
    mockUserRatingRepo = {
      findByUserId: vi.fn(),
      save: vi.fn(),
    } as unknown as UserRatingRepository;
    
    mockExternalRatingRepo = {
      findByUserId: vi.fn(),
      findByUserIdAndGameKey: vi.fn(),
      findByGameKey: vi.fn(),
      save: vi.fn(),
      saveMany: vi.fn(),
      delete: vi.fn(),
      exists: vi.fn(),
      findProfilesPaginated: vi.fn(),
    } as unknown as ExternalGameRatingRepository;

    handler = new GetLeagueEligibilityPreviewQueryHandler(
      mockUserRatingRepo,
      mockExternalRatingRepo
    );
  });

  describe('execute', () => {
    it('should evaluate simple platform eligibility - eligible', async () => {
      const userId = 'user-123';
      const leagueId = 'league-456';
      const rules = 'platform.driving >= 55';

      // Create a rating with driver value of 65 directly
      const now = new Date();
      const userRating = UserRating.restore({
        userId,
        driver: { value: 65, confidence: 0.5, sampleSize: 10, trend: 'rising', lastUpdated: now },
        admin: { value: 50, confidence: 0, sampleSize: 0, trend: 'stable', lastUpdated: now },
        steward: { value: 50, confidence: 0, sampleSize: 0, trend: 'stable', lastUpdated: now },
        trust: { value: 50, confidence: 0, sampleSize: 0, trend: 'stable', lastUpdated: now },
        fairness: { value: 50, confidence: 0, sampleSize: 0, trend: 'stable', lastUpdated: now },
        overallReputation: 50,
        calculatorVersion: '1.0',
        createdAt: now,
        updatedAt: now,
      });
      vi.mocked(mockUserRatingRepo.findByUserId).mockResolvedValue(userRating);
      vi.mocked(mockExternalRatingRepo.findByUserId).mockResolvedValue([]);

      const query: GetLeagueEligibilityPreviewQuery = {
        userId,
        leagueId,
        eligibilityRules: rules,
      };

      const result = await handler.execute(query);

      expect(result.eligible).toBe(true);
      expect(result.reasons).toHaveLength(1);
      expect(result.reasons[0]!.target).toBe('platform.driving');
      expect(result.reasons[0]!.failed).toBe(false);
    });

    it('should evaluate simple platform eligibility - not eligible', async () => {
      const userId = 'user-123';
      const leagueId = 'league-456';
      const rules = 'platform.driving >= 75';

      const userRating = UserRating.create(userId);
      // Driving is 50 by default
      vi.mocked(mockUserRatingRepo.findByUserId).mockResolvedValue(userRating);
      vi.mocked(mockExternalRatingRepo.findByUserId).mockResolvedValue([]);

      const query: GetLeagueEligibilityPreviewQuery = {
        userId,
        leagueId,
        eligibilityRules: rules,
      };

      const result = await handler.execute(query);

      expect(result.eligible).toBe(false);
      expect(result.reasons[0]!.failed).toBe(true);
    });

    it('should evaluate external rating eligibility', async () => {
      const userId = 'user-123';
      const leagueId = 'league-456';
      const rules = 'external.iracing.iRating between 2000 2500';

      vi.mocked(mockUserRatingRepo.findByUserId).mockResolvedValue(null);

      const gameKey = GameKey.create('iracing');
      const profile = ExternalGameRatingProfile.create({
        userId: UserId.fromString(userId),
        gameKey,
        ratings: new Map([
          ['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
        ]),
        provenance: ExternalRatingProvenance.create({ source: 'iRacing API', lastSyncedAt: new Date() }),
      });
      vi.mocked(mockExternalRatingRepo.findByUserId).mockResolvedValue([profile]);

      const query: GetLeagueEligibilityPreviewQuery = {
        userId,
        leagueId,
        eligibilityRules: rules,
      };

      const result = await handler.execute(query);

      expect(result.eligible).toBe(true);
      expect(result.reasons[0]!.target).toBe('external.iracing.iRating');
    });

    it('should evaluate complex AND conditions', async () => {
      const userId = 'user-123';
      const leagueId = 'league-456';
      const rules = 'platform.driving >= 55 AND external.iracing.iRating >= 2000';

      // Create a rating with driver value of 65 directly
      const now = new Date();
      const userRating = UserRating.restore({
        userId,
        driver: { value: 65, confidence: 0.5, sampleSize: 10, trend: 'rising', lastUpdated: now },
        admin: { value: 50, confidence: 0, sampleSize: 0, trend: 'stable', lastUpdated: now },
        steward: { value: 50, confidence: 0, sampleSize: 0, trend: 'stable', lastUpdated: now },
        trust: { value: 50, confidence: 0, sampleSize: 0, trend: 'stable', lastUpdated: now },
        fairness: { value: 50, confidence: 0, sampleSize: 0, trend: 'stable', lastUpdated: now },
        overallReputation: 50,
        calculatorVersion: '1.0',
        createdAt: now,
        updatedAt: now,
      });
      vi.mocked(mockUserRatingRepo.findByUserId).mockResolvedValue(userRating);

      const gameKey = GameKey.create('iracing');
      const profile = ExternalGameRatingProfile.create({
        userId: UserId.fromString(userId),
        gameKey,
        ratings: new Map([
          ['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
        ]),
        provenance: ExternalRatingProvenance.create({ source: 'iRacing API', lastSyncedAt: new Date() }),
      });
      vi.mocked(mockExternalRatingRepo.findByUserId).mockResolvedValue([profile]);

      const query: GetLeagueEligibilityPreviewQuery = {
        userId,
        leagueId,
        eligibilityRules: rules,
      };

      const result = await handler.execute(query);

      expect(result.eligible).toBe(true);
      expect(result.reasons).toHaveLength(2);
      expect(result.reasons.every(r => !r.failed)).toBe(true);
    });

    it('should evaluate OR conditions with at least one passing', async () => {
      const userId = 'user-123';
      const leagueId = 'league-456';
      const rules = 'platform.driving >= 75 OR external.iracing.iRating >= 2000';

      const userRating = UserRating.create(userId);
      vi.mocked(mockUserRatingRepo.findByUserId).mockResolvedValue(userRating);

      const gameKey = GameKey.create('iracing');
      const profile = ExternalGameRatingProfile.create({
        userId: UserId.fromString(userId),
        gameKey,
        ratings: new Map([
          ['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
        ]),
        provenance: ExternalRatingProvenance.create({ source: 'iRacing API', lastSyncedAt: new Date() }),
      });
      vi.mocked(mockExternalRatingRepo.findByUserId).mockResolvedValue([profile]);

      const query: GetLeagueEligibilityPreviewQuery = {
        userId,
        leagueId,
        eligibilityRules: rules,
      };

      const result = await handler.execute(query);

      expect(result.eligible).toBe(true);
      expect(result.reasons.filter(r => !r.failed)).toHaveLength(1);
    });

    it('should include context in metadata', async () => {
      const userId = 'user-123';
      const leagueId = 'league-456';
      const rules = 'platform.driving >= 55';

      vi.mocked(mockUserRatingRepo.findByUserId).mockResolvedValue(null);
      vi.mocked(mockExternalRatingRepo.findByUserId).mockResolvedValue([]);

      const query: GetLeagueEligibilityPreviewQuery = {
        userId,
        leagueId,
        eligibilityRules: rules,
      };

      const result = await handler.execute(query);

      expect(result.metadata?.userId).toBe(userId);
      expect(result.metadata?.filter).toBe(rules);
    });
  });
});
