import { EventEmitter } from 'events';

export interface RankChangeEvent {
  userId: string;
  username: string;
  oldRank: number;
  newRank: number;
  score: number;
  timestamp: Date;
}

export interface AchievementUnlockEvent {
  userId: string;
  username: string;
  achievement: string;
  rarity: 'common' | 'rare' | 'epic' | 'legendary';
  timestamp: Date;
}

export interface LeaderboardSubscription {
  userId: string;
  leaderboardType: 'global' | 'game' | 'seasonal' | 'friends';
  filters?: {
    gameId?: string;
    seasonId?: string;
    period?: 'daily' | 'weekly' | 'monthly' | 'all-time';
  };
}

export interface PlayerConnection {
  userId: string;
  socketId: string;
  subscriptions: LeaderboardSubscription[];
  connectedAt: Date;
  lastActivity: Date;
}

/**
 * Leaderboard WebSocket Service
 * Manages real-time leaderboard updates and player connections
 */
export class LeaderboardWebSocketService extends EventEmitter {
  private connections: Map<string, PlayerConnection> = new Map();
  private rankChangeQueue: RankChangeEvent[] = [];
  private achievementQueue: AchievementUnlockEvent[] = [];
  private broadcastInterval: NodeJS.Timeout | null = null;

  constructor() {
    super();
    console.log('[LeaderboardWebSocket] Service initialized');
    this.startBroadcastInterval();
  }

  /**
   * Register a new player connection
   */
  registerConnection(userId: string, socketId: string): PlayerConnection {
    const connection: PlayerConnection = {
      userId,
      socketId,
      subscriptions: [],
      connectedAt: new Date(),
      lastActivity: new Date(),
    };

    this.connections.set(socketId, connection);

    this.emit('playerConnected', { userId, socketId });
    console.log(`[LeaderboardWebSocket] Player ${userId} connected (${socketId})`);

    return connection;
  }

  /**
   * Unregister a player connection
   */
  unregisterConnection(socketId: string): void {
    const connection = this.connections.get(socketId);
    if (connection) {
      this.connections.delete(socketId);
      this.emit('playerDisconnected', { userId: connection.userId, socketId });
      console.log(`[LeaderboardWebSocket] Player ${connection.userId} disconnected`);
    }
  }

  /**
   * Subscribe to leaderboard updates
   */
  subscribe(socketId: string, subscription: LeaderboardSubscription): boolean {
    const connection = this.connections.get(socketId);
    if (!connection) return false;

    connection.subscriptions.push(subscription);
    this.emit('subscriptionAdded', { socketId, subscription });

    console.log(`[LeaderboardWebSocket] ${connection.userId} subscribed to ${subscription.leaderboardType}`);

    return true;
  }

  /**
   * Unsubscribe from leaderboard updates
   */
  unsubscribe(socketId: string, leaderboardType: string): boolean {
    const connection = this.connections.get(socketId);
    if (!connection) return false;

    connection.subscriptions = connection.subscriptions.filter((s) => s.leaderboardType !== leaderboardType);

    console.log(`[LeaderboardWebSocket] ${connection.userId} unsubscribed from ${leaderboardType}`);

    return true;
  }

  /**
   * Queue a rank change event
   */
  queueRankChange(event: RankChangeEvent): void {
    this.rankChangeQueue.push(event);
    this.emit('rankChangeQueued', event);
  }

  /**
   * Queue an achievement unlock event
   */
  queueAchievementUnlock(event: AchievementUnlockEvent): void {
    this.achievementQueue.push(event);
    this.emit('achievementQueued', event);
  }

  /**
   * Broadcast rank changes to subscribed players
   */
  private broadcastRankChanges(): void {
    if (this.rankChangeQueue.length === 0) return;

    const events = [...this.rankChangeQueue];
    this.rankChangeQueue = [];

    for (const connection of this.connections.values()) {
      // Check if player is subscribed to relevant leaderboards
      const hasGlobalSubscription = connection.subscriptions.some((s) => s.leaderboardType === 'global');
      const hasGameSubscription = connection.subscriptions.some((s) => s.leaderboardType === 'game');
      const hasSeasonalSubscription = connection.subscriptions.some((s) => s.leaderboardType === 'seasonal');

      if (hasGlobalSubscription || hasGameSubscription || hasSeasonalSubscription) {
        this.emit('rankChangeUpdate', {
          socketId: connection.socketId,
          events,
        });
      }
    }
  }

  /**
   * Broadcast achievement unlocks to subscribed players
   */
  private broadcastAchievementUnlocks(): void {
    if (this.achievementQueue.length === 0) return;

    const events = [...this.achievementQueue];
    this.achievementQueue = [];

    for (const connection of this.connections.values()) {
      // Broadcast to all connected players
      this.emit('achievementUpdate', {
        socketId: connection.socketId,
        events,
      });
    }
  }

  /**
   * Start broadcast interval
   */
  private startBroadcastInterval(): void {
    this.broadcastInterval = setInterval(() => {
      this.broadcastRankChanges();
      this.broadcastAchievementUnlocks();
    }, 1000); // Broadcast every second
  }

  /**
   * Get active connections count
   */
  getActiveConnectionsCount(): number {
    return this.connections.size;
  }

  /**
   * Get connections for a user
   */
  getUserConnections(userId: string): PlayerConnection[] {
    return Array.from(this.connections.values()).filter((c) => c.userId === userId);
  }

  /**
   * Get all subscriptions for a leaderboard type
   */
  getSubscriptionsForType(leaderboardType: string): LeaderboardSubscription[] {
    const subscriptions: LeaderboardSubscription[] = [];

    for (const connection of this.connections.values()) {
      subscriptions.push(...connection.subscriptions.filter((s) => s.leaderboardType === leaderboardType));
    }

    return subscriptions;
  }

  /**
   * Update player activity
   */
  updateActivity(socketId: string): void {
    const connection = this.connections.get(socketId);
    if (connection) {
      connection.lastActivity = new Date();
    }
  }

  /**
   * Get connection stats
   */
  getStats() {
    const totalConnections = this.connections.size;
    const totalSubscriptions = Array.from(this.connections.values()).reduce((sum, c) => sum + c.subscriptions.length, 0);
    const queuedRankChanges = this.rankChangeQueue.length;
    const queuedAchievements = this.achievementQueue.length;

    return {
      totalConnections,
      totalSubscriptions,
      queuedRankChanges,
      queuedAchievements,
      uptime: new Date(),
    };
  }

  /**
   * Cleanup inactive connections
   */
  cleanupInactiveConnections(inactivityThresholdMs: number = 5 * 60 * 1000): number {
    const now = new Date();
    let cleaned = 0;

    for (const [socketId, connection] of this.connections.entries()) {
      const inactivityTime = now.getTime() - connection.lastActivity.getTime();

      if (inactivityTime > inactivityThresholdMs) {
        this.unregisterConnection(socketId);
        cleaned++;
      }
    }

    if (cleaned > 0) {
      console.log(`[LeaderboardWebSocket] Cleaned up ${cleaned} inactive connections`);
    }

    return cleaned;
  }

  /**
   * Clear all data (for testing)
   */
  clear(): void {
    this.connections.clear();
    this.rankChangeQueue = [];
    this.achievementQueue = [];
    if (this.broadcastInterval) {
      clearInterval(this.broadcastInterval);
      this.broadcastInterval = null;
    }
    console.log('[LeaderboardWebSocket] Service cleared');
  }

  /**
   * Destroy service
   */
  destroy(): void {
    if (this.broadcastInterval) {
      clearInterval(this.broadcastInterval);
    }
    this.clear();
    console.log('[LeaderboardWebSocket] Service destroyed');
  }
}

export const leaderboardWebSocketService = new LeaderboardWebSocketService();
