import { EventEmitter } from 'events';

export type TimePeriod = 'daily' | 'weekly' | 'monthly' | 'all-time';

export interface LeaderboardEntry {
  rank: number;
  userId: string;
  username: string;
  score: number;
  wins: number;
  winRate: number;
  level: number;
  badge?: string;
  lastUpdated: Date;
}

export interface LeaderboardFilter {
  period: TimePeriod;
  gameId?: string;
  limit?: number;
}

export interface FilteredLeaderboard {
  period: TimePeriod;
  gameId?: string;
  entries: LeaderboardEntry[];
  totalPlayers: number;
  generatedAt: Date;
}

class LeaderboardFilterService extends EventEmitter {
  private leaderboards: Map<string, LeaderboardEntry[]> = new Map();
  private filterCache: Map<string, FilteredLeaderboard> = new Map();
  private lastUpdate: Map<string, Date> = new Map();

  /**
   * Add or update a leaderboard entry
   */
  updateEntry(key: string, entry: LeaderboardEntry): void {
    if (!this.leaderboards.has(key)) {
      this.leaderboards.set(key, []);
    }

    const entries = this.leaderboards.get(key)!;
    const existingIndex = entries.findIndex(e => e.userId === entry.userId);

    if (existingIndex >= 0) {
      entries[existingIndex] = entry;
    } else {
      entries.push(entry);
    }

    // Sort by score descending and update ranks
    entries.sort((a, b) => b.score - a.score);
    entries.forEach((e, index) => {
      e.rank = index + 1;
    });

    // Invalidate cache
    this.invalidateCache(key);
    this.lastUpdate.set(key, new Date());

    this.emit('leaderboard-updated', { key, entry });
  }

  /**
   * Get filtered leaderboard
   */
  getLeaderboard(filter: LeaderboardFilter): FilteredLeaderboard {
    const cacheKey = this.getCacheKey(filter);
    const cached = this.filterCache.get(cacheKey);

    if (cached && this.isCacheValid(cacheKey)) {
      return cached;
    }

    const key = this.getLeaderboardKey(filter.period, filter.gameId);
    const entries = this.leaderboards.get(key) || [];
    const limit = filter.limit || 100;

    const filtered: FilteredLeaderboard = {
      period: filter.period,
      gameId: filter.gameId,
      entries: entries.slice(0, limit),
      totalPlayers: entries.length,
      generatedAt: new Date(),
    };

    this.filterCache.set(cacheKey, filtered);
    return filtered;
  }

  /**
   * Get top players across all games
   */
  getTopPlayers(period: TimePeriod, limit: number = 10): LeaderboardEntry[] {
    const key = this.getLeaderboardKey(period);
    const entries = this.leaderboards.get(key) || [];
    return entries.slice(0, limit);
  }

  /**
   * Get game-specific leaderboard
   */
  getGameLeaderboard(gameId: string, period: TimePeriod, limit: number = 10): LeaderboardEntry[] {
    const key = this.getLeaderboardKey(period, gameId);
    const entries = this.leaderboards.get(key) || [];
    return entries.slice(0, limit);
  }

  /**
   * Get player rank in global leaderboard
   */
  getPlayerGlobalRank(userId: string, period: TimePeriod): number | null {
    const key = this.getLeaderboardKey(period);
    const entries = this.leaderboards.get(key) || [];
    const entry = entries.find(e => e.userId === userId);
    return entry?.rank || null;
  }

  /**
   * Get player rank in game-specific leaderboard
   */
  getPlayerGameRank(userId: string, gameId: string, period: TimePeriod): number | null {
    const key = this.getLeaderboardKey(period, gameId);
    const entries = this.leaderboards.get(key) || [];
    const entry = entries.find(e => e.userId === userId);
    return entry?.rank || null;
  }

  /**
   * Get player's nearby competitors
   */
  getNearbyCompetitors(userId: string, period: TimePeriod, range: number = 5): LeaderboardEntry[] {
    const key = this.getLeaderboardKey(period);
    const entries = this.leaderboards.get(key) || [];
    const playerIndex = entries.findIndex(e => e.userId === userId);

    if (playerIndex === -1) return [];

    const start = Math.max(0, playerIndex - range);
    const end = Math.min(entries.length, playerIndex + range + 1);

    return entries.slice(start, end);
  }

  /**
   * Get leaderboard statistics
   */
  getLeaderboardStats(period: TimePeriod): {
    totalPlayers: number;
    topScore: number;
    averageScore: number;
    medianScore: number;
    topWinRate: number;
    averageWinRate: number;
  } {
    const key = this.getLeaderboardKey(period);
    const entries = this.leaderboards.get(key) || [];

    if (entries.length === 0) {
      return {
        totalPlayers: 0,
        topScore: 0,
        averageScore: 0,
        medianScore: 0,
        topWinRate: 0,
        averageWinRate: 0,
      };
    }

    const scores = entries.map(e => e.score).sort((a, b) => a - b);
    const winRates = entries.map(e => e.winRate);

    return {
      totalPlayers: entries.length,
      topScore: entries[0]?.score || 0,
      averageScore: scores.reduce((a, b) => a + b, 0) / scores.length,
      medianScore: scores[Math.floor(scores.length / 2)],
      topWinRate: Math.max(...winRates),
      averageWinRate: winRates.reduce((a, b) => a + b, 0) / winRates.length,
    };
  }

  /**
   * Get leaderboard comparison between periods
   */
  comparePeriods(userId: string, period1: TimePeriod, period2: TimePeriod): {
    period1Rank: number | null;
    period2Rank: number | null;
    rankChange: number;
    improvement: boolean;
  } {
    const rank1 = this.getPlayerGlobalRank(userId, period1);
    const rank2 = this.getPlayerGlobalRank(userId, period2);

    return {
      period1Rank: rank1,
      period2Rank: rank2,
      rankChange: rank1 && rank2 ? rank1 - rank2 : 0,
      improvement: rank1 && rank2 ? rank2 < rank1 : false,
    };
  }

  /**
   * Get trending players (biggest rank improvements)
   */
  getTrendingPlayers(period: TimePeriod, previousPeriod: TimePeriod, limit: number = 10): Array<{
    entry: LeaderboardEntry;
    rankChange: number;
    improvement: boolean;
  }> {
    const key = this.getLeaderboardKey(period);
    const entries = this.leaderboards.get(key) || [];

    const trending = entries
      .map(entry => {
        const previousRank = this.getPlayerGlobalRank(entry.userId, previousPeriod);
        const rankChange = previousRank ? previousRank - entry.rank : 0;
        return {
          entry,
          rankChange,
          improvement: rankChange > 0,
        };
      })
      .sort((a, b) => b.rankChange - a.rankChange)
      .slice(0, limit);

    return trending;
  }

  /**
   * Reset leaderboard for a period
   */
  resetLeaderboard(period: TimePeriod, gameId?: string): void {
    const key = this.getLeaderboardKey(period, gameId);
    this.leaderboards.delete(key);
    this.invalidateCache(key);
    this.emit('leaderboard-reset', { period, gameId });
  }

  /**
   * Clear all leaderboards
   */
  clear(): void {
    this.leaderboards.clear();
    this.filterCache.clear();
    this.lastUpdate.clear();
  }

  // Private helper methods

  private getLeaderboardKey(period: TimePeriod, gameId?: string): string {
    return gameId ? `${period}-${gameId}` : period;
  }

  private getCacheKey(filter: LeaderboardFilter): string {
    return `cache-${filter.period}-${filter.gameId || 'global'}-${filter.limit || 100}`;
  }

  private invalidateCache(key: string): void {
    // Invalidate all cache entries related to this key
    const keysToDelete: string[] = [];
    for (const cacheKey of this.filterCache.keys()) {
      if (cacheKey.includes(key)) {
        keysToDelete.push(cacheKey);
      }
    }
    keysToDelete.forEach(k => this.filterCache.delete(k));
  }

  private isCacheValid(cacheKey: string): boolean {
    // Cache is valid for 5 minutes
    const cached = this.filterCache.get(cacheKey);
    if (!cached) return false;

    const age = Date.now() - cached.generatedAt.getTime();
    return age < 5 * 60 * 1000;
  }
}

export const leaderboardFilterService = new LeaderboardFilterService();
