/**
 * Game Leaderboards Service
 * Manages real-time leaderboards for games with weekly/monthly resets
 */

export type LeaderboardPeriod = 'daily' | 'weekly' | 'monthly' | 'alltime';
export type LeaderboardType = 'biggest_wins' | 'longest_sessions' | 'highest_multipliers' | 'most_plays';

interface LeaderboardEntry {
  rank: number;
  userId: string;
  username: string;
  value: number;
  gameId: string;
  timestamp: Date;
  details?: Record<string, any>;
}

interface GameLeaderboard {
  gameId: string;
  gameName: string;
  period: LeaderboardPeriod;
  type: LeaderboardType;
  entries: LeaderboardEntry[];
  lastReset: Date;
  nextReset: Date;
  totalEntries: number;
}

interface UserLeaderboardPosition {
  gameId: string;
  rank: number;
  value: number;
  percentile: number;
}

class GameLeaderboardsService {
  private leaderboards: Map<string, GameLeaderboard> = new Map();
  private userStats: Map<string, Map<string, any>> = new Map();

  /**
   * Create a new leaderboard
   */
  createLeaderboard(
    gameId: string,
    gameName: string,
    period: LeaderboardPeriod,
    type: LeaderboardType
  ): GameLeaderboard {
    const key = `${gameId}_${period}_${type}`;

    const leaderboard: GameLeaderboard = {
      gameId,
      gameName,
      period,
      type,
      entries: [],
      lastReset: new Date(),
      nextReset: this.calculateNextReset(period),
      totalEntries: 0,
    };

    this.leaderboards.set(key, leaderboard);
    return leaderboard;
  }

  /**
   * Calculate next reset time based on period
   */
  private calculateNextReset(period: LeaderboardPeriod): Date {
    const now = new Date();

    switch (period) {
      case 'daily':
        return new Date(now.getTime() + 24 * 60 * 60 * 1000);
      case 'weekly':
        const nextWeek = new Date(now);
        nextWeek.setDate(nextWeek.getDate() + (7 - nextWeek.getDay()));
        return nextWeek;
      case 'monthly':
        return new Date(now.getFullYear(), now.getMonth() + 1, 1);
      case 'alltime':
        return new Date(2099, 12, 31); // Far future
      default:
        return new Date(now.getTime() + 24 * 60 * 60 * 1000);
    }
  }

  /**
   * Record a player achievement
   */
  recordAchievement(
    gameId: string,
    userId: string,
    username: string,
    type: LeaderboardType,
    value: number,
    details?: Record<string, any>
  ): void {
    // Record for all periods
    const periods: LeaderboardPeriod[] = ['daily', 'weekly', 'monthly', 'alltime'];

    periods.forEach((period) => {
      const key = `${gameId}_${period}_${type}`;
      let leaderboard = this.leaderboards.get(key);

      if (!leaderboard) {
        leaderboard = this.createLeaderboard(gameId, '', period, type);
      }

      // Check if user already has entry
      const existingIndex = leaderboard.entries.findIndex((e) => e.userId === userId);

      if (existingIndex >= 0) {
        // Update if new value is better
        if (value > leaderboard.entries[existingIndex].value) {
          leaderboard.entries[existingIndex] = {
            rank: 0,
            userId,
            username,
            value,
            gameId,
            timestamp: new Date(),
            details,
          };
        }
      } else {
        // Add new entry
        leaderboard.entries.push({
          rank: 0,
          userId,
          username,
          value,
          gameId,
          timestamp: new Date(),
          details,
        });
      }

      // Sort and update ranks
      leaderboard.entries.sort((a, b) => b.value - a.value);
      leaderboard.entries.forEach((entry, index) => {
        entry.rank = index + 1;
      });

      leaderboard.totalEntries = leaderboard.entries.length;
    });
  }

  /**
   * Get leaderboard for a game
   */
  getLeaderboard(
    gameId: string,
    period: LeaderboardPeriod,
    type: LeaderboardType,
    limit: number = 100
  ): LeaderboardEntry[] {
    const key = `${gameId}_${period}_${type}`;
    const leaderboard = this.leaderboards.get(key);

    if (!leaderboard) {
      return [];
    }

    return leaderboard.entries.slice(0, limit);
  }

  /**
   * Get user's position on leaderboard
   */
  getUserPosition(
    gameId: string,
    userId: string,
    period: LeaderboardPeriod,
    type: LeaderboardType
  ): UserLeaderboardPosition | null {
    const key = `${gameId}_${period}_${type}`;
    const leaderboard = this.leaderboards.get(key);

    if (!leaderboard) {
      return null;
    }

    const entry = leaderboard.entries.find((e) => e.userId === userId);

    if (!entry) {
      return null;
    }

    const percentile = ((leaderboard.totalEntries - entry.rank) / leaderboard.totalEntries) * 100;

    return {
      gameId,
      rank: entry.rank,
      value: entry.value,
      percentile,
    };
  }

  /**
   * Get top players across all games
   */
  getTopPlayers(limit: number = 100): Array<{ userId: string; username: string; totalScore: number }> {
    const playerStats = new Map<string, { username: string; totalScore: number }>();

    this.leaderboards.forEach((leaderboard) => {
      leaderboard.entries.forEach((entry) => {
        if (!playerStats.has(entry.userId)) {
          playerStats.set(entry.userId, { username: entry.username, totalScore: 0 });
        }
        const stats = playerStats.get(entry.userId)!;
        stats.totalScore += entry.value;
      });
    });

    return Array.from(playerStats.entries())
      .map(([userId, data]) => ({ userId, ...data }))
      .sort((a, b) => b.totalScore - a.totalScore)
      .slice(0, limit);
  }

  /**
   * Get leaderboard statistics
   */
  getLeaderboardStats(gameId: string, period: LeaderboardPeriod, type: LeaderboardType): {
    totalPlayers: number;
    averageScore: number;
    topScore: number;
    bottomScore: number;
  } | null {
    const key = `${gameId}_${period}_${type}`;
    const leaderboard = this.leaderboards.get(key);

    if (!leaderboard || leaderboard.entries.length === 0) {
      return null;
    }

    const values = leaderboard.entries.map((e) => e.value);
    const averageScore = values.reduce((a, b) => a + b, 0) / values.length;

    return {
      totalPlayers: leaderboard.entries.length,
      averageScore,
      topScore: values[0],
      bottomScore: values[values.length - 1],
    };
  }

  /**
   * Check if leaderboards need reset
   */
  checkAndResetLeaderboards(): void {
    const now = new Date();

    this.leaderboards.forEach((leaderboard) => {
      if (now >= leaderboard.nextReset && leaderboard.period !== 'alltime') {
        // Archive current leaderboard
        // In production, save to database

        // Reset
        leaderboard.entries = [];
        leaderboard.lastReset = now;
        leaderboard.nextReset = this.calculateNextReset(leaderboard.period);
        leaderboard.totalEntries = 0;
      }
    });
  }

  /**
   * Get leaderboard by game type
   */
  getLeaderboardsByGameType(gameId: string): Map<string, GameLeaderboard> {
    const result = new Map<string, GameLeaderboard>();

    this.leaderboards.forEach((leaderboard, key) => {
      if (leaderboard.gameId === gameId) {
        result.set(key, leaderboard);
      }
    });

    return result;
  }

  /**
   * Get trending games (most active leaderboards)
   */
  getTrendingGames(limit: number = 10): Array<{ gameId: string; gameName: string; totalPlayers: number }> {
    const gameStats = new Map<string, { gameName: string; totalPlayers: number }>();

    this.leaderboards.forEach((leaderboard) => {
      if (!gameStats.has(leaderboard.gameId)) {
        gameStats.set(leaderboard.gameId, { gameName: leaderboard.gameName, totalPlayers: 0 });
      }
      const stats = gameStats.get(leaderboard.gameId)!;
      stats.totalPlayers += leaderboard.entries.length;
    });

    return Array.from(gameStats.entries())
      .map(([gameId, data]) => ({ gameId, ...data }))
      .sort((a, b) => b.totalPlayers - a.totalPlayers)
      .slice(0, limit);
  }

  /**
   * Get player achievements
   */
  getPlayerAchievements(userId: string): Array<{
    gameId: string;
    type: LeaderboardType;
    rank: number;
    value: number;
    period: LeaderboardPeriod;
  }> {
    const achievements: Array<{
      gameId: string;
      type: LeaderboardType;
      rank: number;
      value: number;
      period: LeaderboardPeriod;
    }> = [];

    this.leaderboards.forEach((leaderboard) => {
      const entry = leaderboard.entries.find((e) => e.userId === userId);
      if (entry) {
        achievements.push({
          gameId: leaderboard.gameId,
          type: leaderboard.type,
          rank: entry.rank,
          value: entry.value,
          period: leaderboard.period,
        });
      }
    });

    return achievements;
  }
}

export const gameLeaderboardsService = new GameLeaderboardsService();
