import { EventEmitter } from 'events';

export type SeasonalFrequency = 'weekly' | 'monthly' | 'quarterly' | 'yearly';

export interface Season {
  id: string;
  name: string;
  startDate: Date;
  endDate: Date;
  frequency: SeasonalFrequency;
  rewards: SeasonalReward[];
  active: boolean;
  archived: boolean;
}

export interface SeasonalReward {
  rank: number; // 1-10 for top 10
  coinReward: number;
  xpReward: number;
  badge: string;
  title: string;
}

export interface SeasonalLeaderboardEntry {
  userId: string;
  username: string;
  seasonId: string;
  score: number;
  rank: number;
  previousRank?: number;
  badge?: string;
  rewardClaimed: boolean;
}

export interface SeasonalArchive {
  seasonId: string;
  seasonName: string;
  endDate: Date;
  topPlayers: SeasonalLeaderboardEntry[];
  totalParticipants: number;
}

/**
 * Seasonal Leaderboard System
 * Manages seasonal leaderboards with automatic resets and rewards
 */
export class SeasonalLeaderboardSystem extends EventEmitter {
  private seasons: Map<string, Season> = new Map();
  private leaderboards: Map<string, SeasonalLeaderboardEntry[]> = new Map();
  private archives: SeasonalArchive[] = [];
  private currentSeasonId: string | null = null;

  constructor() {
    super();
    console.log('[SeasonalLeaderboard] System initialized');
  }

  /**
   * Create a new season
   */
  createSeason(
    name: string,
    startDate: Date,
    endDate: Date,
    frequency: SeasonalFrequency,
    rewards: SeasonalReward[]
  ): Season {
    const seasonId = `season-${Date.now()}`;

    const season: Season = {
      id: seasonId,
      name,
      startDate,
      endDate,
      frequency,
      rewards,
      active: true,
      archived: false,
    };

    this.seasons.set(seasonId, season);
    this.leaderboards.set(seasonId, []);

    if (!this.currentSeasonId) {
      this.currentSeasonId = seasonId;
    }

    this.emit('seasonCreated', season);
    console.log(`[SeasonalLeaderboard] Season created: ${name} (${seasonId})`);

    return season;
  }

  /**
   * Get current active season
   */
  getCurrentSeason(): Season | null {
    if (!this.currentSeasonId) return null;
    return this.seasons.get(this.currentSeasonId) || null;
  }

  /**
   * Add or update player on seasonal leaderboard
   */
  updatePlayerScore(seasonId: string, userId: string, username: string, score: number): SeasonalLeaderboardEntry {
    const leaderboard = this.leaderboards.get(seasonId) || [];

    // Find existing entry
    let entry = leaderboard.find((e) => e.userId === userId);

    if (!entry) {
      entry = {
        userId,
        username,
        seasonId,
        score,
        rank: 0,
        rewardClaimed: false,
      };
      leaderboard.push(entry);
    } else {
      entry.score = Math.max(entry.score, score);
    }

    // Re-sort and update ranks
    leaderboard.sort((a, b) => b.score - a.score);
    leaderboard.forEach((e, idx) => {
      e.previousRank = e.rank;
      e.rank = idx + 1;
    });

    this.leaderboards.set(seasonId, leaderboard);

    this.emit('playerScoreUpdated', { seasonId, userId, score, rank: entry.rank });

    return entry;
  }

  /**
   * Get seasonal leaderboard
   */
  getSeasonalLeaderboard(seasonId: string, limit: number = 100): SeasonalLeaderboardEntry[] {
    const leaderboard = this.leaderboards.get(seasonId) || [];
    return leaderboard.slice(0, limit);
  }

  /**
   * Get player's seasonal rank
   */
  getPlayerSeasonalRank(seasonId: string, userId: string): SeasonalLeaderboardEntry | null {
    const leaderboard = this.leaderboards.get(seasonId) || [];
    return leaderboard.find((e) => e.userId === userId) || null;
  }

  /**
   * Get seasonal rewards for a rank
   */
  getSeasonalReward(seasonId: string, rank: number): SeasonalReward | null {
    const season = this.seasons.get(seasonId);
    if (!season) return null;

    return season.rewards.find((r) => r.rank === rank) || null;
  }

  /**
   * End current season and archive results
   */
  endSeason(seasonId: string): SeasonalArchive {
    const season = this.seasons.get(seasonId);
    if (!season) {
      throw new Error(`Season ${seasonId} not found`);
    }

    const leaderboard = this.leaderboards.get(seasonId) || [];

    const archive: SeasonalArchive = {
      seasonId,
      seasonName: season.name,
      endDate: new Date(),
      topPlayers: leaderboard.slice(0, 100),
      totalParticipants: leaderboard.length,
    };

    this.archives.push(archive);

    // Mark season as inactive
    season.active = false;
    season.archived = true;

    this.emit('seasonEnded', archive);
    console.log(`[SeasonalLeaderboard] Season ended: ${season.name}`);

    return archive;
  }

  /**
   * Distribute seasonal rewards
   */
  distributeSeasonalRewards(seasonId: string): Array<{ userId: string; reward: SeasonalReward }> {
    const season = this.seasons.get(seasonId);
    if (!season) {
      throw new Error(`Season ${seasonId} not found`);
    }

    const leaderboard = this.leaderboards.get(seasonId) || [];
    const distributed: Array<{ userId: string; reward: SeasonalReward }> = [];

    // Distribute rewards to top players
    for (const entry of leaderboard) {
      const reward = season.rewards.find((r) => r.rank === entry.rank);

      if (reward && !entry.rewardClaimed) {
        entry.rewardClaimed = true;
        entry.badge = reward.badge;

        distributed.push({
          userId: entry.userId,
          reward,
        });

        this.emit('rewardDistributed', {
          userId: entry.userId,
          seasonId,
          rank: entry.rank,
          reward,
        });
      }
    }

    console.log(`[SeasonalLeaderboard] Distributed ${distributed.length} rewards for season ${seasonId}`);

    return distributed;
  }

  /**
   * Start new season automatically
   */
  startNewSeason(
    name: string,
    frequency: SeasonalFrequency,
    rewards: SeasonalReward[]
  ): Season {
    const now = new Date();
    let endDate = new Date(now);

    // Calculate end date based on frequency
    switch (frequency) {
      case 'weekly':
        endDate.setDate(endDate.getDate() + 7);
        break;
      case 'monthly':
        endDate.setMonth(endDate.getMonth() + 1);
        break;
      case 'quarterly':
        endDate.setMonth(endDate.getMonth() + 3);
        break;
      case 'yearly':
        endDate.setFullYear(endDate.getFullYear() + 1);
        break;
    }

    return this.createSeason(name, now, endDate, frequency, rewards);
  }

  /**
   * Get seasonal leaderboard history
   */
  getSeasonalHistory(limit: number = 10): SeasonalArchive[] {
    return this.archives.slice(-limit).reverse();
  }

  /**
   * Get all seasons
   */
  getAllSeasons(): Season[] {
    return Array.from(this.seasons.values());
  }

  /**
   * Get player's seasonal history
   */
  getPlayerSeasonalHistory(userId: string): Array<{ season: Season; entry: SeasonalLeaderboardEntry }> {
    const history: Array<{ season: Season; entry: SeasonalLeaderboardEntry }> = [];

    for (const [seasonId, leaderboard] of this.leaderboards.entries()) {
      const entry = leaderboard.find((e) => e.userId === userId);
      const season = this.seasons.get(seasonId);

      if (entry && season) {
        history.push({ season, entry });
      }
    }

    return history.sort((a, b) => b.season.endDate.getTime() - a.season.endDate.getTime());
  }

  /**
   * Get seasonal badge for player
   */
  getPlayerSeasonalBadge(userId: string, seasonId: string): string | null {
    const entry = this.getPlayerSeasonalRank(seasonId, userId);
    return entry?.badge || null;
  }

  /**
   * Calculate seasonal statistics
   */
  getSeasonalStats(seasonId: string) {
    const season = this.seasons.get(seasonId);
    const leaderboard = this.leaderboards.get(seasonId) || [];

    if (!season) return null;

    const scores = leaderboard.map((e) => e.score);
    const avgScore = scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0;
    const maxScore = Math.max(...scores, 0);
    const minScore = Math.min(...scores, 0);

    return {
      seasonId,
      seasonName: season.name,
      totalParticipants: leaderboard.length,
      averageScore: avgScore,
      maxScore,
      minScore,
      daysRemaining: Math.ceil((season.endDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)),
    };
  }

  /**
   * Reset all data (for testing)
   */
  clear(): void {
    this.seasons.clear();
    this.leaderboards.clear();
    this.archives = [];
    this.currentSeasonId = null;
    console.log('[SeasonalLeaderboard] System cleared');
  }
}

export const seasonalLeaderboardSystem = new SeasonalLeaderboardSystem();
