import { EventEmitter } from 'events';

interface TournamentPlayer {
  playerId: string;
  playerName: string;
  score: number;
  rank: number;
  previousRank?: number;
}

interface RankUpdate {
  playerId: string;
  newRank: number;
  previousRank: number;
  scoreChange: number;
  timestamp: Date;
}

interface TournamentUpdate {
  tournamentId: string;
  type: 'rank_change' | 'player_join' | 'player_leave' | 'score_update' | 'tournament_end';
  data: any;
  timestamp: Date;
}

export class TournamentWebSocketService extends EventEmitter {
  private activeTournaments: Map<string, TournamentPlayer[]> = new Map();
  private playerConnections: Map<string, Set<string>> = new Map(); // playerId -> tournamentIds
  private updateQueue: TournamentUpdate[] = [];
  private queueInterval: NodeJS.Timeout | null = null;

  constructor() {
    super();
    this.startUpdateQueue();
  }

  /**
   * Start broadcasting queued updates at regular intervals
   */
  private startUpdateQueue() {
    this.queueInterval = setInterval(() => {
      if (this.updateQueue.length > 0) {
        const updates = [...this.updateQueue];
        this.updateQueue = [];
        this.emit('batch_updates', updates);
      }
    }, 1000); // Batch updates every second
  }

  /**
   * Register a player joining a tournament
   */
  registerPlayerJoin(tournamentId: string, player: TournamentPlayer) {
    if (!this.activeTournaments.has(tournamentId)) {
      this.activeTournaments.set(tournamentId, []);
    }

    const tournament = this.activeTournaments.get(tournamentId)!;
    tournament.push(player);

    // Track player connection
    if (!this.playerConnections.has(player.playerId)) {
      this.playerConnections.set(player.playerId, new Set());
    }
    this.playerConnections.get(player.playerId)!.add(tournamentId);

    // Queue update
    this.updateQueue.push({
      tournamentId,
      type: 'player_join',
      data: {
        playerId: player.playerId,
        playerName: player.playerName,
        totalPlayers: tournament.length,
      },
      timestamp: new Date(),
    });

    this.emit('player_joined', { tournamentId, player });
  }

  /**
   * Register a player leaving a tournament
   */
  registerPlayerLeave(tournamentId: string, playerId: string) {
    const tournament = this.activeTournaments.get(tournamentId);
    if (!tournament) return;

    const playerIndex = tournament.findIndex((p) => p.playerId === playerId);
    if (playerIndex !== -1) {
      const player = tournament[playerIndex];
      tournament.splice(playerIndex, 1);

      // Remove from player connections
      this.playerConnections.get(playerId)?.delete(tournamentId);

      // Queue update
      this.updateQueue.push({
        tournamentId,
        type: 'player_leave',
        data: {
          playerId,
          totalPlayers: tournament.length,
        },
        timestamp: new Date(),
      });

      this.emit('player_left', { tournamentId, playerId });
    }
  }

  /**
   * Update player score and recalculate rankings
   */
  updatePlayerScore(tournamentId: string, playerId: string, scoreIncrease: number) {
    const tournament = this.activeTournaments.get(tournamentId);
    if (!tournament) return;

    const player = tournament.find((p) => p.playerId === playerId);
    if (!player) return;

    const previousScore = player.score;
    const previousRank = player.rank;

    player.score += scoreIncrease;

    // Recalculate rankings
    this.recalculateRankings(tournamentId);

    const newRank = player.rank;

    // Queue update
    this.updateQueue.push({
      tournamentId,
      type: 'score_update',
      data: {
        playerId,
        playerName: player.playerName,
        newScore: player.score,
        scoreChange: scoreIncrease,
        newRank,
        previousRank,
        rankChanged: newRank !== previousRank,
      },
      timestamp: new Date(),
    });

    // Emit rank change event if rank changed
    if (newRank !== previousRank) {
      this.emit('rank_changed', {
        tournamentId,
        playerId,
        newRank,
        previousRank,
        scoreChange: scoreIncrease,
      });
    }

    this.emit('score_updated', {
      tournamentId,
      playerId,
      newScore: player.score,
      scoreChange: scoreIncrease,
    });
  }

  /**
   * Recalculate rankings for a tournament
   */
  private recalculateRankings(tournamentId: string) {
    const tournament = this.activeTournaments.get(tournamentId);
    if (!tournament) return;

    // Sort by score descending
    tournament.sort((a, b) => b.score - a.score);

    // Update ranks
    tournament.forEach((player, index) => {
      player.previousRank = player.rank;
      player.rank = index + 1;
    });
  }

  /**
   * Get current leaderboard for a tournament
   */
  getLeaderboard(tournamentId: string): TournamentPlayer[] {
    const tournament = this.activeTournaments.get(tournamentId);
    return tournament ? [...tournament] : [];
  }

  /**
   * Get player position in tournament
   */
  getPlayerPosition(tournamentId: string, playerId: string): TournamentPlayer | null {
    const tournament = this.activeTournaments.get(tournamentId);
    if (!tournament) return null;

    return tournament.find((p) => p.playerId === playerId) || null;
  }

  /**
   * Get all tournaments a player is in
   */
  getPlayerTournaments(playerId: string): string[] {
    return Array.from(this.playerConnections.get(playerId) || new Set());
  }

  /**
   * End tournament and return final rankings
   */
  endTournament(tournamentId: string): TournamentPlayer[] {
    const tournament = this.activeTournaments.get(tournamentId);
    if (!tournament) return [];

    const finalRankings = [...tournament];

    // Queue update
    this.updateQueue.push({
      tournamentId,
      type: 'tournament_end',
      data: {
        finalRankings,
        totalPlayers: finalRankings.length,
      },
      timestamp: new Date(),
    });

    // Clean up
    this.activeTournaments.delete(tournamentId);

    this.emit('tournament_ended', { tournamentId, finalRankings });

    return finalRankings;
  }

  /**
   * Get tournament statistics
   */
  getTournamentStats(tournamentId: string) {
    const tournament = this.activeTournaments.get(tournamentId);
    if (!tournament) return null;

    const totalScore = tournament.reduce((sum, p) => sum + p.score, 0);
    const avgScore = totalScore / tournament.length;
    const maxScore = Math.max(...tournament.map((p) => p.score));
    const minScore = Math.min(...tournament.map((p) => p.score));

    return {
      tournamentId,
      totalPlayers: tournament.length,
      totalScore,
      avgScore,
      maxScore,
      minScore,
      topPlayer: tournament[0],
    };
  }

  /**
   * Cleanup and shutdown
   */
  shutdown() {
    if (this.queueInterval) {
      clearInterval(this.queueInterval);
    }
    this.activeTournaments.clear();
    this.playerConnections.clear();
    this.updateQueue = [];
    this.removeAllListeners();
  }
}

export default TournamentWebSocketService;
