/**
 * Tests for GetLeagueEligibilityPreviewQuery
 */

import { GetLeagueEligibilityPreviewQuery, GetLeagueEligibilityPreviewQueryHandler } from './GetLeagueEligibilityPreviewQuery';
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';

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

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

    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';

      const userRating = UserRating.create(userId);
      // Update driving to 65
      const updatedRating = userRating.updateDriverRating(65);
      mockUserRatingRepo.findByUserId.mockResolvedValue(updatedRating);
      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
      mockUserRatingRepo.findByUserId.mockResolvedValue(userRating);
      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';

      mockUserRatingRepo.findByUserId.mockResolvedValue(null);

      const gameKey = GameKey.create('iracing');
      const profile = ExternalGameRatingProfile.create({
        userId: { toString: () => userId } as any,
        gameKey,
        ratings: new Map([
          ['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
        ]),
        provenance: ExternalRatingProvenance.create({ source: 'iRacing API', lastSyncedAt: new Date() }),
      });
      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';

      const userRating = UserRating.create(userId);
      const updatedRating = userRating.updateDriverRating(65);
      mockUserRatingRepo.findByUserId.mockResolvedValue(updatedRating);

      const gameKey = GameKey.create('iracing');
      const profile = ExternalGameRatingProfile.create({
        userId: { toString: () => userId } as any,
        gameKey,
        ratings: new Map([
          ['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
        ]),
        provenance: ExternalRatingProvenance.create({ source: 'iRacing API', lastSyncedAt: new Date() }),
      });
      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);
      mockUserRatingRepo.findByUserId.mockResolvedValue(userRating);

      const gameKey = GameKey.create('iracing');
      const profile = ExternalGameRatingProfile.create({
        userId: { toString: () => userId } as any,
        gameKey,
        ratings: new Map([
          ['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
        ]),
        provenance: ExternalRatingProvenance.create({ source: 'iRacing API', lastSyncedAt: new Date() }),
      });
      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';

      mockUserRatingRepo.findByUserId.mockResolvedValue(null);
      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);
    });
  });
});
