import { EventEmitter } from 'events';

export interface MultiplayerGame {
  id: string;
  gameId: string;
  name: string;
  maxPlayers: number;
  currentPlayers: number;
  status: 'waiting' | 'playing' | 'finished';
  createdAt: Date;
  startedAt?: Date;
  finishedAt?: Date;
  players: MultiplayerPlayer[];
  leaderboard: LeaderboardEntry[];
  prizePool: number;
  winnerIds: string[];
}

export interface MultiplayerPlayer {
  playerId: string;
  playerName: string;
  joinedAt: Date;
  score: number;
  wager: number;
  status: 'waiting' | 'playing' | 'finished';
  position?: number;
  winnings?: number;
}

export interface LeaderboardEntry {
  playerId: string;
  playerName: string;
  score: number;
  position: number;
  winnings: number;
  timestamp: Date;
}

export interface TournamentConfig {
  id: string;
  name: string;
  gameId: string;
  startDate: Date;
  endDate: Date;
  entryFee: number;
  maxParticipants: number;
  prizeDistribution: Record<number, number>; // position -> prize
  status: 'upcoming' | 'active' | 'finished';
  participants: string[];
  leaderboard: LeaderboardEntry[];
  totalPrizePool: number;
}

class MultiplayerGameSystem extends EventEmitter {
  private games = new Map<string, MultiplayerGame>();
  private tournaments = new Map<string, TournamentConfig>();
  private playerSessions = new Map<string, string>(); // playerId -> gameId

  /**
   * Create multiplayer game session
   */
  createMultiplayerGame(
    gameId: string,
    name: string,
    maxPlayers: number,
    prizePool: number
  ): MultiplayerGame {
    const id = `mp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const game: MultiplayerGame = {
      id,
      gameId,
      name,
      maxPlayers,
      currentPlayers: 0,
      status: 'waiting',
      createdAt: new Date(),
      players: [],
      leaderboard: [],
      prizePool,
      winnerIds: [],
    };

    this.games.set(id, game);
    this.emit('game:created', game);
    return game;
  }

  /**
   * Join multiplayer game
   */
  joinGame(gameId: string, playerId: string, playerName: string, wager: number): MultiplayerGame | null {
    const game = this.games.get(gameId);
    if (!game || game.status !== 'waiting' || game.currentPlayers >= game.maxPlayers) {
      return null;
    }

    const player: MultiplayerPlayer = {
      playerId,
      playerName,
      joinedAt: new Date(),
      score: 0,
      wager,
      status: 'waiting',
    };

    game.players.push(player);
    game.currentPlayers++;
    this.playerSessions.set(playerId, gameId);

    this.emit('player:joined', { gameId, player });

    // Auto-start if full
    if (game.currentPlayers === game.maxPlayers) {
      this.startGame(gameId);
    }

    return game;
  }

  /**
   * Leave multiplayer game
   */
  leaveGame(gameId: string, playerId: string): MultiplayerGame | null {
    const game = this.games.get(gameId);
    if (!game) return null;

    const playerIndex = game.players.findIndex(p => p.playerId === playerId);
    if (playerIndex === -1) return null;

    game.players.splice(playerIndex, 1);
    game.currentPlayers--;
    this.playerSessions.delete(playerId);

    this.emit('player:left', { gameId, playerId });
    return game;
  }

  /**
   * Start multiplayer game
   */
  startGame(gameId: string): MultiplayerGame | null {
    const game = this.games.get(gameId);
    if (!game || game.status !== 'waiting') return null;

    game.status = 'playing';
    game.startedAt = new Date();

    game.players.forEach(p => (p.status = 'playing'));

    this.emit('game:started', game);
    return game;
  }

  /**
   * Update player score
   */
  updatePlayerScore(gameId: string, playerId: string, scoreIncrease: number): MultiplayerGame | null {
    const game = this.games.get(gameId);
    if (!game) return null;

    const player = game.players.find(p => p.playerId === playerId);
    if (!player) return null;

    player.score += scoreIncrease;
    this.updateLeaderboard(gameId);

    this.emit('score:updated', { gameId, playerId, newScore: player.score });
    return game;
  }

  /**
   * Finish multiplayer game
   */
  finishGame(gameId: string): MultiplayerGame | null {
    const game = this.games.get(gameId);
    if (!game || game.status !== 'playing') return null;

    game.status = 'finished';
    game.finishedAt = new Date();

    // Determine winners and distribute prizes
    this.distributePrizes(gameId);

    game.players.forEach(p => (p.status = 'finished'));

    this.emit('game:finished', game);
    return game;
  }

  /**
   * Update leaderboard
   */
  private updateLeaderboard(gameId: string): void {
    const game = this.games.get(gameId);
    if (!game) return;

    const sorted = [...game.players].sort((a, b) => b.score - a.score);

    game.leaderboard = sorted.map((player, index) => ({
      playerId: player.playerId,
      playerName: player.playerName,
      score: player.score,
      position: index + 1,
      winnings: player.winnings || 0,
      timestamp: new Date(),
    }));
  }

  /**
   * Distribute prizes
   */
  private distributePrizes(gameId: string): void {
    const game = this.games.get(gameId);
    if (!game) return;

    const sorted = [...game.players].sort((a, b) => b.score - a.score);

    // Simple prize distribution: top 3 get prizes
    const prizePercentages = [0.5, 0.3, 0.2]; // 50%, 30%, 20%

    sorted.forEach((player, index) => {
      if (index < prizePercentages.length) {
        const prize = game.prizePool * prizePercentages[index];
        player.winnings = prize;
        player.position = index + 1;
        game.winnerIds.push(player.playerId);
      }
    });

    this.updateLeaderboard(gameId);
  }

  /**
   * Get game details
   */
  getGame(gameId: string): MultiplayerGame | null {
    return this.games.get(gameId) || null;
  }

  /**
   * List active games
   */
  listActiveGames(): MultiplayerGame[] {
    return Array.from(this.games.values()).filter(g => g.status === 'playing');
  }

  /**
   * List waiting games
   */
  listWaitingGames(): MultiplayerGame[] {
    return Array.from(this.games.values()).filter(g => g.status === 'waiting');
  }

  /**
   * Create tournament
   */
  createTournament(config: Omit<TournamentConfig, 'id' | 'participants' | 'leaderboard' | 'status'>): TournamentConfig {
    const id = `tour_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    const tournament: TournamentConfig = {
      ...config,
      id,
      participants: [],
      leaderboard: [],
      status: 'upcoming',
    };

    this.tournaments.set(id, tournament);
    this.emit('tournament:created', tournament);
    return tournament;
  }

  /**
   * Join tournament
   */
  joinTournament(tournamentId: string, playerId: string, playerName: string): TournamentConfig | null {
    const tournament = this.tournaments.get(tournamentId);
    if (!tournament || tournament.status !== 'upcoming' || tournament.participants.length >= tournament.maxParticipants) {
      return null;
    }

    tournament.participants.push(playerId);
    tournament.totalPrizePool += tournament.entryFee;

    this.emit('tournament:joined', { tournamentId, playerId });
    return tournament;
  }

  /**
   * Start tournament
   */
  startTournament(tournamentId: string): TournamentConfig | null {
    const tournament = this.tournaments.get(tournamentId);
    if (!tournament || tournament.status !== 'upcoming') return null;

    tournament.status = 'active';
    this.emit('tournament:started', tournament);
    return tournament;
  }

  /**
   * Finish tournament
   */
  finishTournament(tournamentId: string, finalLeaderboard: LeaderboardEntry[]): TournamentConfig | null {
    const tournament = this.tournaments.get(tournamentId);
    if (!tournament || tournament.status !== 'active') return null;

    tournament.status = 'finished';
    tournament.leaderboard = finalLeaderboard;

    this.emit('tournament:finished', tournament);
    return tournament;
  }

  /**
   * Get tournament details
   */
  getTournament(tournamentId: string): TournamentConfig | null {
    return this.tournaments.get(tournamentId) || null;
  }

  /**
   * List active tournaments
   */
  listActiveTournaments(): TournamentConfig[] {
    return Array.from(this.tournaments.values()).filter(t => t.status === 'active');
  }

  /**
   * Get player's current game
   */
  getPlayerCurrentGame(playerId: string): MultiplayerGame | null {
    const gameId = this.playerSessions.get(playerId);
    return gameId ? this.getGame(gameId) : null;
  }

  /**
   * Get player tournament history
   */
  getPlayerTournamentHistory(playerId: string): TournamentConfig[] {
    return Array.from(this.tournaments.values()).filter(t =>
      t.participants.includes(playerId)
    );
  }

  /**
   * Get global leaderboard
   */
  getGlobalLeaderboard(limit: number = 100): LeaderboardEntry[] {
    const allEntries: LeaderboardEntry[] = [];

    for (const game of this.games.values()) {
      allEntries.push(...game.leaderboard);
    }

    for (const tournament of this.tournaments.values()) {
      allEntries.push(...tournament.leaderboard);
    }

    return allEntries
      .sort((a, b) => b.score - a.score)
      .slice(0, limit);
  }

  /**
   * Get player statistics
   */
  getPlayerStats(playerId: string): {
    gamesPlayed: number;
    tournamentsPlayed: number;
    totalWinnings: number;
    avgScore: number;
    wins: number;
  } {
    let gamesPlayed = 0;
    let totalWinnings = 0;
    let totalScore = 0;
    let wins = 0;

    for (const game of this.games.values()) {
      const player = game.players.find(p => p.playerId === playerId);
      if (player) {
        gamesPlayed++;
        totalScore += player.score;
        if (player.winnings) {
          totalWinnings += player.winnings;
          wins++;
        }
      }
    }

    const tournamentsPlayed = this.getPlayerTournamentHistory(playerId).length;

    return {
      gamesPlayed,
      tournamentsPlayed,
      totalWinnings,
      avgScore: gamesPlayed > 0 ? totalScore / gamesPlayed : 0,
      wins,
    };
  }

  /**
   * Export game results
   */
  exportGameResults(gameId: string): string | null {
    const game = this.getGame(gameId);
    if (!game) return null;

    return JSON.stringify(game, null, 2);
  }
}

export const multiplayerGameSystem = new MultiplayerGameSystem();
