import { db } from './db.ts';

export type LeaderboardType = 'achievements' | 'challenges' | 'events' | 'referrals' | 'winnings' | 'playtime';
export type LeaderboardPeriod = 'daily' | 'weekly' | 'monthly' | 'alltime';

export interface LeaderboardEntry {
  rank: number;
  playerId: number;
  playerName: string;
  score: number;
  change: number; // +/- from previous period
  badge?: string;
}

export interface LeaderboardStats {
  type: LeaderboardType;
  period: LeaderboardPeriod;
  topPlayers: LeaderboardEntry[];
  playerRank?: number;
  playerScore?: number;
  lastUpdated: Date;
}

/**
 * Leaderboards Manager
 */
export class LeaderboardsManager {
  private leaderboards: Map<string, LeaderboardEntry[]> = new Map();
  private lastUpdated: Map<string, Date> = new Map();

  /**
   * Get leaderboard key
   */
  private getKey(type: LeaderboardType, period: LeaderboardPeriod): string {
    return `${type}_${period}`;
  }

  /**
   * Update leaderboards
   */
  async updateLeaderboards(): Promise<void> {
    // Update all leaderboard types and periods
    const types: LeaderboardType[] = ['achievements', 'challenges', 'events', 'referrals', 'winnings', 'playtime'];
    const periods: LeaderboardPeriod[] = ['daily', 'weekly', 'monthly', 'alltime'];

    for (const type of types) {
      for (const period of periods) {
        await this.updateLeaderboard(type, period);
      }
    }
  }

  /**
   * Update specific leaderboard
   */
  private async updateLeaderboard(type: LeaderboardType, period: LeaderboardPeriod): Promise<void> {
    const key = this.getKey(type, period);
    const entries: LeaderboardEntry[] = [];

    // Get date range based on period
    const now = new Date();
    let startDate = new Date();

    switch (period) {
      case 'daily':
        startDate.setDate(now.getDate() - 1);
        break;
      case 'weekly':
        startDate.setDate(now.getDate() - 7);
        break;
      case 'monthly':
        startDate.setMonth(now.getMonth() - 1);
        break;
      case 'alltime':
        startDate = new Date(0);
        break;
    }

    // Calculate scores based on type
    const scores = new Map<number, { name: string; score: number }>();

    switch (type) {
      case 'achievements':
        // Score based on achievements unlocked
        // In real implementation, query achievement data
        break;

      case 'challenges':
        // Score based on challenges completed
        // In real implementation, query challenge data
        break;

      case 'events':
        // Score based on event participation and completion
        // In real implementation, query event data
        break;

      case 'referrals':
        // Score based on referral earnings
        // In real implementation, query referral data
        break;

      case 'winnings':
        // Score based on total winnings
        // In real implementation, query wallet transaction data
        break;

      case 'playtime':
        // Score based on total playtime
        // In real implementation, query session data
        break;
    }

    // Convert to leaderboard entries
    let rank = 1;
    for (const [playerId, data] of scores.entries()) {
      entries.push({
        rank,
        playerId,
        playerName: data.name,
        score: data.score,
        change: 0, // Calculate from previous period
        badge: this.getBadge(rank),
      });
      rank++;
    }

    // Store leaderboard
    this.leaderboards.set(key, entries);
    this.lastUpdated.set(key, new Date());
  }

  /**
   * Get badge for rank
   */
  private getBadge(rank: number): string | undefined {
    switch (rank) {
      case 1:
        return '🥇';
      case 2:
        return '🥈';
      case 3:
        return '🥉';
      default:
        if (rank <= 10) return '⭐';
        if (rank <= 50) return '✨';
        return undefined;
    }
  }

  /**
   * Get leaderboard
   */
  async getLeaderboard(
    type: LeaderboardType,
    period: LeaderboardPeriod,
    limit: number = 100
  ): Promise<LeaderboardStats> {
    const key = this.getKey(type, period);
    const entries = this.leaderboards.get(key) || [];

    return {
      type,
      period,
      topPlayers: entries.slice(0, limit),
      lastUpdated: this.lastUpdated.get(key) || new Date(),
    };
  }

  /**
   * Get player rank
   */
  async getPlayerRank(
    playerId: number,
    type: LeaderboardType,
    period: LeaderboardPeriod
  ): Promise<LeaderboardEntry | null> {
    const key = this.getKey(type, period);
    const entries = this.leaderboards.get(key) || [];

    return entries.find((e) => e.playerId === playerId) || null;
  }

  /**
   * Get player leaderboard stats
   */
  async getPlayerStats(playerId: number): Promise<{
    achievements: LeaderboardEntry | null;
    challenges: LeaderboardEntry | null;
    events: LeaderboardEntry | null;
    referrals: LeaderboardEntry | null;
    winnings: LeaderboardEntry | null;
    playtime: LeaderboardEntry | null;
  }> {
    return {
      achievements: await this.getPlayerRank(playerId, 'achievements', 'alltime'),
      challenges: await this.getPlayerRank(playerId, 'challenges', 'alltime'),
      events: await this.getPlayerRank(playerId, 'events', 'alltime'),
      referrals: await this.getPlayerRank(playerId, 'referrals', 'alltime'),
      winnings: await this.getPlayerRank(playerId, 'winnings', 'alltime'),
      playtime: await this.getPlayerRank(playerId, 'playtime', 'alltime'),
    };
  }

  /**
   * Get top players across all categories
   */
  async getTopPlayers(limit: number = 10): Promise<Array<{
    rank: number;
    playerId: number;
    playerName: string;
    totalScore: number;
    badges: string[];
  }>> {
    const topPlayers = new Map<number, { name: string; score: number; badges: Set<string> }>();

    // Aggregate scores from all leaderboards
    for (const entries of this.leaderboards.values()) {
      for (const entry of entries.slice(0, 100)) {
        if (!topPlayers.has(entry.playerId)) {
          topPlayers.set(entry.playerId, { name: entry.playerName, score: 0, badges: new Set() });
        }

        const player = topPlayers.get(entry.playerId)!;
        player.score += entry.score;

        if (entry.badge) {
          player.badges.add(entry.badge);
        }
      }
    }

    // Sort by total score
    const sorted = Array.from(topPlayers.entries())
      .sort((a, b) => b[1].score - a[1].score)
      .slice(0, limit);

    return sorted.map(([playerId, data], index) => ({
      rank: index + 1,
      playerId,
      playerName: data.name,
      totalScore: data.score,
      badges: Array.from(data.badges),
    }));
  }

  /**
   * Get leaderboard history
   */
  async getLeaderboardHistory(
    type: LeaderboardType,
    playerId: number,
    periods: LeaderboardPeriod[] = ['daily', 'weekly', 'monthly', 'alltime']
  ): Promise<Array<{ period: LeaderboardPeriod; rank: number; score: number }>> {
    const history: Array<{ period: LeaderboardPeriod; rank: number; score: number }> = [];

    for (const period of periods) {
      const entry = await this.getPlayerRank(playerId, type, period);
      if (entry) {
        history.push({
          period,
          rank: entry.rank,
          score: entry.score,
        });
      }
    }

    return history;
  }

  /**
   * Get trending players (biggest rank improvements)
   */
  async getTrendingPlayers(limit: number = 10): Promise<Array<{
    rank: number;
    playerId: number;
    playerName: string;
    improvement: number;
    currentRank: number;
  }>> {
    const trending: Array<{
      playerId: number;
      playerName: string;
      improvement: number;
      currentRank: number;
    }> = [];

    // Get players with biggest positive rank changes
    for (const entries of this.leaderboards.values()) {
      for (const entry of entries) {
        if (entry.change > 0) {
          const existing = trending.find((t) => t.playerId === entry.playerId);
          if (existing) {
            existing.improvement += entry.change;
          } else {
            trending.push({
              playerId: entry.playerId,
              playerName: entry.playerName,
              improvement: entry.change,
              currentRank: entry.rank,
            });
          }
        }
      }
    }

    // Sort by improvement
    trending.sort((a, b) => b.improvement - a.improvement);

    return trending.slice(0, limit).map((entry, index) => ({
      rank: index + 1,
      ...entry,
    }));
  }

  /**
   * Get leaderboard categories
   */
  getLeaderboardCategories(): Array<{
    type: LeaderboardType;
    name: string;
    icon: string;
    description: string;
  }> {
    return [
      {
        type: 'achievements',
        name: 'Achievements',
        icon: '🏆',
        description: 'Most achievements unlocked',
      },
      {
        type: 'challenges',
        name: 'Challenges',
        icon: '🎯',
        description: 'Most challenges completed',
      },
      {
        type: 'events',
        name: 'Events',
        icon: '🎉',
        description: 'Most events participated',
      },
      {
        type: 'referrals',
        name: 'Referrals',
        icon: '👥',
        description: 'Most referral earnings',
      },
      {
        type: 'winnings',
        name: 'Winnings',
        icon: '💰',
        description: 'Highest total winnings',
      },
      {
        type: 'playtime',
        name: 'Playtime',
        icon: '⏱️',
        description: 'Most playtime hours',
      },
    ];
  }

  /**
   * Get leaderboard stats
   */
  getStats(): {
    totalLeaderboards: number;
    totalEntries: number;
    lastUpdateTime: string;
  } {
    let totalEntries = 0;
    for (const entries of this.leaderboards.values()) {
      totalEntries += entries.length;
    }

    const lastUpdate = Array.from(this.lastUpdated.values()).sort((a, b) => b.getTime() - a.getTime())[0];

    return {
      totalLeaderboards: this.leaderboards.size,
      totalEntries,
      lastUpdateTime: lastUpdate ? lastUpdate.toISOString() : 'Never',
    };
  }
}

export const leaderboardsManager = new LeaderboardsManager();
