/**
 * Advanced Systems Tests
 * Comprehensive tests for push notifications, tournaments, and affiliate systems
 */

import { describe, it, expect, beforeEach } from 'vitest';
import {
  createPushNotification,
  createPrizeNotification,
  createTierPromotionNotification,
  createBonusNotification,
  shouldSendNotification,
  isInQuietHours,
  markNotificationAsRead,
  calculateNotificationStats,
  createDefaultPreferences,
  type PushNotification,
  type NotificationPreferences,
} from './pushNotificationSystem';
import {
  createTournament,
  registerPlayerInTournament,
  updatePlayerScore,
  rankLeaderboard,
  shouldEndTournament,
  endTournament,
  getTournamentLeaderboard,
  calculateTournamentStats,
  generateTournamentReport,
  type Tournament,
} from './tournamentSystem';
import {
  createAffiliateAccount,
  createReferral,
  convertReferral,
  updateReferralSpend,
  getAffiliateTier,
  updateAffiliateTier,
  createPayout,
  calculateAffiliateStats,
  generateAffiliateReport,
  type AffiliateAccount,
  type AffiliateReferral,
} from './affiliateSystem';

describe('Advanced Systems', () => {
  describe('Push Notification System', () => {
    let notification: PushNotification;
    let preferences: NotificationPreferences;

    beforeEach(() => {
      notification = createPushNotification(
        1,
        'prize_won',
        'Prize Won!',
        'Congratulations!',
        { amount: 100 }
      );
      preferences = createDefaultPreferences(1);
    });

    it('should create prize notification', () => {
      const prizeNotif = createPrizeNotification(1, 500, 1, 'game1');
      expect(prizeNotif.type).toBe('prize_won');
      expect(prizeNotif.data.amount).toBe(500);
    });

    it('should create tier promotion notification', () => {
      const tierNotif = createTierPromotionNotification(1, 'gold', ['benefit1']);
      expect(tierNotif.type).toBe('tier_promotion');
      expect(tierNotif.data.newTier).toBe('gold');
    });

    it('should create bonus notification', () => {
      const bonusNotif = createBonusNotification(1, 'free_spins', 5, 'game1');
      expect(bonusNotif.type).toBe('bonus_triggered');
      expect(bonusNotif.data.multiplier).toBe(5);
    });

    it('should check if notification should be sent', () => {
      expect(shouldSendNotification(preferences, 'prize_won', 'normal')).toBe(true);

      preferences.prizeNotifications = false;
      expect(shouldSendNotification(preferences, 'prize_won', 'normal')).toBe(false);

      expect(shouldSendNotification(preferences, 'prize_won', 'urgent')).toBe(true);
    });

    it('should mark notification as read', () => {
      expect(notification.readAt).toBeUndefined();
      markNotificationAsRead(notification);
      expect(notification.readAt).toBeDefined();
    });

    it('should calculate notification statistics', () => {
      const notif1 = createPushNotification(1, 'prize_won', 'Prize', 'Won', {});
      const notif2 = createPushNotification(1, 'bonus_triggered', 'Bonus', 'Triggered', {});

      notif1.deliveredAt = new Date();
      markNotificationAsRead(notif1);

      const stats = calculateNotificationStats([notif1, notif2]);

      expect(stats.totalSent).toBe(2);
      expect(stats.totalDelivered).toBe(1);
      expect(stats.totalRead).toBe(1);
      expect(stats.deliveryRate).toBeCloseTo(50, 1);
    });
  });

  describe('Tournament System', () => {
    let tournament: Tournament;

    beforeEach(() => {
      tournament = createTournament(
        'Weekly Challenge',
        'weekly',
        ['game1', 'game2'],
        'highest_score',
        1000,
        new Date(),
        new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        {
          allowedGames: ['game1', 'game2'],
          bonusMultiplier: 1.5,
          tiebreaker: 'earliest_score',
          requiresVIP: false,
        }
      );
      tournament.status = 'active';
    });

    it('should register player in tournament', () => {
      const success = registerPlayerInTournament(tournament, 1, 'Player1');
      expect(success).toBe(true);
      expect(tournament.participants).toBe(1);
      expect(tournament.leaderboard.length).toBe(1);
    });

    it('should prevent duplicate registration', () => {
      registerPlayerInTournament(tournament, 1, 'Player1');
      const success = registerPlayerInTournament(tournament, 1, 'Player1');
      expect(success).toBe(false);
      expect(tournament.participants).toBe(1);
    });

    it('should update player score', () => {
      registerPlayerInTournament(tournament, 1, 'Player1');
      const success = updatePlayerScore(tournament, 1, 100);
      expect(success).toBe(true);
      expect(tournament.leaderboard[0].score).toBe(100);
    });

    it('should apply score multiplier', () => {
      registerPlayerInTournament(tournament, 1, 'Player1');
      updatePlayerScore(tournament, 1, 100, 1.5);
      expect(tournament.leaderboard[0].score).toBe(150);
    });

    it('should rank leaderboard correctly', () => {
      registerPlayerInTournament(tournament, 1, 'Player1');
      registerPlayerInTournament(tournament, 2, 'Player2');
      registerPlayerInTournament(tournament, 3, 'Player3');

      updatePlayerScore(tournament, 1, 100);
      updatePlayerScore(tournament, 2, 200);
      updatePlayerScore(tournament, 3, 150);

      rankLeaderboard(tournament);

      expect(tournament.leaderboard[0].userId).toBe(2);
      expect(tournament.leaderboard[0].rank).toBe(1);
      expect(tournament.leaderboard[1].userId).toBe(3);
      expect(tournament.leaderboard[1].rank).toBe(2);
    });

    it('should check if tournament should end', () => {
      expect(shouldEndTournament(tournament)).toBe(false);

      tournament.endDate = new Date(Date.now() - 1000);
      expect(shouldEndTournament(tournament)).toBe(true);
    });

    it('should end tournament and distribute prizes', () => {
      registerPlayerInTournament(tournament, 1, 'Player1');
      registerPlayerInTournament(tournament, 2, 'Player2');

      updatePlayerScore(tournament, 1, 200);
      updatePlayerScore(tournament, 2, 100);

      rankLeaderboard(tournament);
      endTournament(tournament);

      expect(tournament.status).toBe('ended');
      expect(tournament.leaderboard[0].prize).toBeDefined();
      expect(tournament.leaderboard[0].prize).toBeGreaterThan(0);
    });

    it('should calculate tournament statistics', () => {
      registerPlayerInTournament(tournament, 1, 'Player1');
      registerPlayerInTournament(tournament, 2, 'Player2');

      updatePlayerScore(tournament, 1, 100);
      updatePlayerScore(tournament, 2, 50);

      const stats = calculateTournamentStats(tournament);

      expect(stats.totalParticipants).toBe(2);
      expect(stats.highestScore).toBe(100);
      expect(stats.lowestScore).toBe(50);
      expect(stats.averageScore).toBe(75);
    });

    it('should generate tournament report', () => {
      registerPlayerInTournament(tournament, 1, 'Player1');
      updatePlayerScore(tournament, 1, 100);
      rankLeaderboard(tournament);
      endTournament(tournament);

      const report = generateTournamentReport(tournament);

      expect(report.tournamentName).toBe('Weekly Challenge');
      expect(report.totalParticipants).toBe(1);
      expect(report.totalPrizePool).toBe(1000);
    });
  });

  describe('Affiliate System', () => {
    let affiliate: AffiliateAccount;
    let referral: AffiliateReferral;

    beforeEach(() => {
      affiliate = createAffiliateAccount(1, 'affiliate1', 'affiliate@example.com', 'bank_transfer');
      referral = createReferral(affiliate.id, 2, 'https://coinkrazy.com');
    });

    it('should create affiliate account', () => {
      expect(affiliate.status).toBe('pending');
      expect(affiliate.tier).toBe('bronze');
      expect(affiliate.commissionRate).toBe(20);
    });

    it('should create referral with code', () => {
      expect(referral.referralCode).toHaveLength(8);
      expect(referral.referralLink).toContain(referral.referralCode);
      expect(referral.status).toBe('pending');
    });

    it('should convert referral', () => {
      convertReferral(referral, 100, affiliate);

      expect(referral.status).toBe('converted');
      expect(referral.firstDepositAmount).toBe(100);
      expect(referral.commission).toBe(20); // 20% of 100
      expect(affiliate.pendingEarnings).toBe(20);
    });

    it('should update referral spend', () => {
      convertReferral(referral, 100, affiliate);
      updateReferralSpend(referral, 50, affiliate);

      expect(referral.totalReferredSpend).toBe(150);
      expect(referral.commission).toBe(30); // 20% of 150
    });

    it('should determine affiliate tier', () => {
      expect(getAffiliateTier(0)).toBe('bronze');
      expect(getAffiliateTier(10)).toBe('silver');
      expect(getAffiliateTier(50)).toBe('gold');
      expect(getAffiliateTier(100)).toBe('platinum');
      expect(getAffiliateTier(250)).toBe('diamond');
    });

    it('should update affiliate tier', () => {
      affiliate.totalReferrals = 50;
      updateAffiliateTier(affiliate);

      expect(affiliate.tier).toBe('gold');
      expect(affiliate.commissionRate).toBe(30);
    });

    it('should create payout', () => {
      convertReferral(referral, 100, affiliate);
      const payout = createPayout(affiliate, [referral.id], 20);

      expect(payout.amount).toBe(20);
      expect(payout.status).toBe('pending');
      expect(payout.commissionIds).toContain(referral.id);
    });

    it('should calculate affiliate statistics', () => {
      convertReferral(referral, 100, affiliate);
      affiliate.totalReferrals = 1;
      affiliate.totalEarnings = 20;

      const stats = calculateAffiliateStats(affiliate, [referral]);

      expect(stats.totalReferrals).toBe(1);
      expect(stats.convertedReferrals).toBe(1);
      expect(stats.conversionRate).toBe(100);
      expect(stats.totalEarnings).toBe(20);
    });

    it('should generate affiliate report', () => {
      convertReferral(referral, 100, affiliate);
      affiliate.totalReferrals = 1;
      affiliate.totalEarnings = 20;

      const report = generateAffiliateReport(affiliate, [referral]);

      expect(report.username).toBe('affiliate1');
      expect(report.tier).toBe('bronze');
      expect(report.totalReferrals).toBe(1);
      expect(report.totalEarnings).toBe(20);
    });
  });

  describe('Cross-System Integration', () => {
    it('should handle tournament with affiliate referrals', () => {
      const tournament = createTournament(
        'Affiliate Tournament',
        'special',
        ['game1'],
        'highest_score',
        1000,
        new Date(),
        new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        {
          allowedGames: ['game1'],
          bonusMultiplier: 2,
          tiebreaker: 'earliest_score',
          requiresVIP: false,
        }
      );
      tournament.status = 'active';

      const affiliate = createAffiliateAccount(1, 'aff1', 'aff@example.com', 'bank_transfer');
      const referral = createReferral(affiliate.id, 2, 'https://coinkrazy.com');

      // Referred player joins tournament
      registerPlayerInTournament(tournament, 2, 'ReferredPlayer');
      updatePlayerScore(tournament, 2, 100);

      // Affiliate earns commission when referred player deposits
      convertReferral(referral, 50, affiliate);

      expect(tournament.participants).toBe(1);
      expect(affiliate.pendingEarnings).toBe(10); // 20% of 50
    });

    it('should send notifications for tournament wins', () => {
      const tournament = createTournament(
        'Test Tournament',
        'weekly',
        ['game1'],
        'highest_score',
        1000,
        new Date(),
        new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        {
          allowedGames: ['game1'],
          bonusMultiplier: 1,
          tiebreaker: 'earliest_score',
          requiresVIP: false,
        }
      );
      tournament.status = 'active';

      registerPlayerInTournament(tournament, 1, 'Player1');
      updatePlayerScore(tournament, 1, 100);
      rankLeaderboard(tournament);
      endTournament(tournament);

      const notification = createPrizeNotification(
        1,
        tournament.leaderboard[0].prize || 0,
        1,
        'tournament'
      );

      expect(notification.type).toBe('prize_won');
      expect(notification.data.amount).toBeGreaterThan(0);
    });
  });
});
