/**
 * Leaderboard Notifications System Tests
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { leaderboardNotifications } from './leaderboardNotifications';

describe('Leaderboard Notifications System', () => {
  beforeEach(() => {
    // Clear notifications before each test
    leaderboardNotifications.clearNotifications(1);
    leaderboardNotifications.clearNotifications(2);
  });

  describe('notifyRankUp', () => {
    it('should create rank up notification', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      const notifications = leaderboardNotifications.getNotifications(1);
      
      expect(notifications.length).toBe(1);
      expect(notifications[0].type).toBe('rank_up');
      expect(notifications[0].oldRank).toBe(50);
      expect(notifications[0].newRank).toBe(45);
    });

    it('should emit rankup event', (done) => {
      leaderboardNotifications.on('rankup', (notification) => {
        expect(notification.type).toBe('rank_up');
        done();
      });

      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
    });

    it('should mark notification as unread', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      const notifications = leaderboardNotifications.getNotifications(1);
      
      expect(notifications[0].read).toBe(false);
    });
  });

  describe('notifyRankDown', () => {
    it('should create rank down notification', () => {
      leaderboardNotifications.notifyRankDown(1, 'neon-nights', 'weekly', 45, 50);
      const notifications = leaderboardNotifications.getNotifications(1);
      
      expect(notifications.length).toBe(1);
      expect(notifications[0].type).toBe('rank_down');
      expect(notifications[0].oldRank).toBe(45);
      expect(notifications[0].newRank).toBe(50);
    });

    it('should emit rankdown event', (done) => {
      leaderboardNotifications.on('rankdown', (notification) => {
        expect(notification.type).toBe('rank_down');
        done();
      });

      leaderboardNotifications.notifyRankDown(1, 'neon-nights', 'weekly', 45, 50);
    });
  });

  describe('notifyTopTen', () => {
    it('should create top 10 notification', () => {
      leaderboardNotifications.notifyTopTen(1, 'neon-nights', 'weekly', 8);
      const notifications = leaderboardNotifications.getNotifications(1);
      
      expect(notifications.length).toBe(1);
      expect(notifications[0].type).toBe('top_10');
      expect(notifications[0].newRank).toBe(8);
    });

    it('should emit top10 event', (done) => {
      leaderboardNotifications.on('top10', (notification) => {
        expect(notification.type).toBe('top_10');
        done();
      });

      leaderboardNotifications.notifyTopTen(1, 'neon-nights', 'weekly', 8);
    });
  });

  describe('notifyTopFifty', () => {
    it('should create top 50 notification', () => {
      leaderboardNotifications.notifyTopFifty(1, 'neon-nights', 'weekly', 35);
      const notifications = leaderboardNotifications.getNotifications(1);
      
      expect(notifications.length).toBe(1);
      expect(notifications[0].type).toBe('top_50');
      expect(notifications[0].newRank).toBe(35);
    });
  });

  describe('notifyTopHundred', () => {
    it('should create top 100 notification', () => {
      leaderboardNotifications.notifyTopHundred(1, 'neon-nights', 'weekly', 75);
      const notifications = leaderboardNotifications.getNotifications(1);
      
      expect(notifications.length).toBe(1);
      expect(notifications[0].type).toBe('top_100');
      expect(notifications[0].newRank).toBe(75);
    });
  });

  describe('notifyBonusTriggered', () => {
    it('should create bonus notification', () => {
      leaderboardNotifications.notifyBonusTriggered(1, 'wheel-spin', 'neon-nights', 2.5);
      const notifications = leaderboardNotifications.getBonusNotifications(1);
      
      expect(notifications.length).toBe(1);
      expect(notifications[0].bonusType).toBe('wheel-spin');
      expect(notifications[0].multiplier).toBe(2.5);
    });

    it('should emit bonus event', (done) => {
      leaderboardNotifications.on('bonus', (notification) => {
        expect(notification.bonusType).toBe('wheel-spin');
        done();
      });

      leaderboardNotifications.notifyBonusTriggered(1, 'wheel-spin', 'neon-nights', 2.5);
    });
  });

  describe('notifyMilestone', () => {
    it('should create milestone notification', () => {
      leaderboardNotifications.notifyMilestone(1, 'neon-nights', '1000 spins completed');
      const notifications = leaderboardNotifications.getNotifications(1);
      
      expect(notifications.length).toBe(1);
      expect(notifications[0].type).toBe('milestone');
    });

    it('should emit milestone event', (done) => {
      leaderboardNotifications.on('milestone', (notification) => {
        expect(notification.type).toBe('milestone');
        done();
      });

      leaderboardNotifications.notifyMilestone(1, 'neon-nights', '1000 spins completed');
    });
  });

  describe('getNotifications', () => {
    it('should return empty array for new user', () => {
      const notifications = leaderboardNotifications.getNotifications(999);
      expect(notifications).toEqual([]);
    });

    it('should respect limit parameter', () => {
      for (let i = 0; i < 10; i++) {
        leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      }

      const notifications = leaderboardNotifications.getNotifications(1, 5);
      expect(notifications.length).toBeLessThanOrEqual(5);
    });

    it('should return notifications in reverse chronological order', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 45, 40);
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 40, 35);

      const notifications = leaderboardNotifications.getNotifications(1);
      expect(notifications[0].newRank).toBe(35);
      expect(notifications[1].newRank).toBe(40);
      expect(notifications[2].newRank).toBe(45);
    });
  });

  describe('getBonusNotifications', () => {
    it('should return only bonus notifications', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      leaderboardNotifications.notifyBonusTriggered(1, 'wheel-spin', 'neon-nights', 2.5);
      leaderboardNotifications.notifyBonusTriggered(1, 'pick-em', 'neon-nights', 3);

      const bonusNotifications = leaderboardNotifications.getBonusNotifications(1);
      expect(bonusNotifications.length).toBe(2);
      bonusNotifications.forEach((n) => {
        expect(n.bonusType).toBeDefined();
      });
    });
  });

  describe('getUnreadCount', () => {
    it('should return 0 for new user', () => {
      const count = leaderboardNotifications.getUnreadCount(999);
      expect(count).toBe(0);
    });

    it('should count unread notifications', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 45, 40);
      leaderboardNotifications.notifyBonusTriggered(1, 'wheel-spin', 'neon-nights', 2.5);

      const count = leaderboardNotifications.getUnreadCount(1);
      expect(count).toBe(3);
    });

    it('should not count read notifications', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      const notifications = leaderboardNotifications.getNotifications(1);
      const notifId = notifications[0].id;

      leaderboardNotifications.markAsRead(1, notifId);
      const count = leaderboardNotifications.getUnreadCount(1);
      expect(count).toBe(0);
    });
  });

  describe('markAsRead', () => {
    it('should mark notification as read', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      const notifications = leaderboardNotifications.getNotifications(1);
      const notifId = notifications[0].id;

      leaderboardNotifications.markAsRead(1, notifId);
      const updated = leaderboardNotifications.getNotifications(1);
      expect(updated[0].read).toBe(true);
    });
  });

  describe('markAllAsRead', () => {
    it('should mark all notifications as read', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      leaderboardNotifications.notifyBonusTriggered(1, 'wheel-spin', 'neon-nights', 2.5);

      leaderboardNotifications.markAllAsRead(1);
      
      const allNotifications = leaderboardNotifications.getAllNotifications(1);
      allNotifications.forEach((n) => {
        expect(n.read).toBe(true);
      });
    });
  });

  describe('clearNotifications', () => {
    it('should remove all notifications for user', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      leaderboardNotifications.notifyBonusTriggered(1, 'wheel-spin', 'neon-nights', 2.5);

      leaderboardNotifications.clearNotifications(1);
      
      const notifications = leaderboardNotifications.getNotifications(1);
      expect(notifications.length).toBe(0);
    });

    it('should not affect other users', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      leaderboardNotifications.notifyRankUp(2, 'neon-nights', 'weekly', 50, 45);

      leaderboardNotifications.clearNotifications(1);
      
      const user1Notifs = leaderboardNotifications.getNotifications(1);
      const user2Notifs = leaderboardNotifications.getNotifications(2);
      
      expect(user1Notifs.length).toBe(0);
      expect(user2Notifs.length).toBe(1);
    });
  });

  describe('getAllNotifications', () => {
    it('should combine leaderboard and bonus notifications', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      leaderboardNotifications.notifyBonusTriggered(1, 'wheel-spin', 'neon-nights', 2.5);
      leaderboardNotifications.notifyMilestone(1, 'neon-nights', '1000 spins');

      const allNotifications = leaderboardNotifications.getAllNotifications(1);
      expect(allNotifications.length).toBe(3);
    });

    it('should sort by timestamp descending', () => {
      leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50, 45);
      leaderboardNotifications.notifyBonusTriggered(1, 'wheel-spin', 'neon-nights', 2.5);

      const allNotifications = leaderboardNotifications.getAllNotifications(1);
      for (let i = 1; i < allNotifications.length; i++) {
        expect(allNotifications[i - 1].timestamp.getTime()).toBeGreaterThanOrEqual(
          allNotifications[i].timestamp.getTime()
        );
      }
    });
  });

  describe('Notification limits', () => {
    it('should maintain max notification limit', () => {
      // Create more than max notifications
      for (let i = 0; i < 150; i++) {
        leaderboardNotifications.notifyRankUp(1, 'neon-nights', 'weekly', 50 + i, 49 + i);
      }

      const notifications = leaderboardNotifications.getNotifications(1, 1000);
      expect(notifications.length).toBeLessThanOrEqual(100);
    });
  });
});
