const { v4: uuidv4 } = require('uuid');
const youtubeAPI = require('./youtube-api');

/**
 * Game state and logic manager for Song Battle application
 */
class GameManager {
  constructor() {
    this.lobbies = new Map(); // Map of lobbyId -> lobby object
    this.playerToLobby = new Map(); // Map of playerId -> lobbyId for quick lookup
  }

  /**
   * Create a new game lobby
   * @param {string} hostId - ID of the host player
   * @param {string} hostName - Name of the host player
   * @returns {Object} New lobby data and ID
   */
  createLobby(hostId, hostName) {
    // Generate a simple 6-character lobby code
    const lobbyId = this._generateLobbyCode();
    
    // Create lobby object
    const lobby = {
      id: lobbyId,
      hostId: hostId,
      state: 'LOBBY', // LOBBY -> SONG_SUBMISSION -> VOTING -> FINISHED
      settings: {
        songsPerPlayer: 3,
        maxPlayers: 10,
        minPlayers: 3,
        requireYoutubeLinks: false
      },
      players: [{
        id: hostId,
        name: hostName,
        isConnected: true,
        isReady: false,
        songs: [],
        songCount: 0
      }],
      songs: [], // All submitted songs
      currentBattle: null,
      battles: [], // History of all battles
      finalWinner: null
    };
    
    // Store lobby and player-to-lobby mapping
    this.lobbies.set(lobbyId, lobby);
    this.playerToLobby.set(hostId, lobbyId);
    
    return { lobby, lobbyId };
  }
  
  /**
   * Join an existing lobby
   * @param {string} playerId - ID of joining player
   * @param {string} playerName - Name of joining player
   * @param {string} lobbyId - ID of lobby to join
   * @returns {Object} Lobby data or error
   */
  joinLobby(playerId, playerName, lobbyId) {
    // Check if lobby exists
    if (!this.lobbies.has(lobbyId)) {
      return { error: 'Lobby not found' };
    }
    
    const lobby = this.lobbies.get(lobbyId);
    
    // Check if lobby is in correct state
    if (lobby.state !== 'LOBBY') {
      return { error: 'Cannot join: game already in progress' };
    }
    
    // Check if player limit is reached
    if (lobby.players.length >= lobby.settings.maxPlayers) {
      return { error: 'Lobby is full' };
    }
    
    // Check if player name is already taken
    if (lobby.players.some(p => p.name === playerName && p.isConnected)) {
      return { error: 'Name already taken' };
    }
    
    // Check if player is rejoining
    const existingPlayerIndex = lobby.players.findIndex(p => p.name === playerName && !p.isConnected);
    if (existingPlayerIndex >= 0) {
      // Update player ID and connection status
      lobby.players[existingPlayerIndex].id = playerId;
      lobby.players[existingPlayerIndex].isConnected = true;
      this.playerToLobby.set(playerId, lobbyId);
      return { lobby, lobbyId };
    }
    
    // Add new player
    lobby.players.push({
      id: playerId,
      name: playerName,
      isConnected: true,
      isReady: false,
      songs: [],
      songCount: 0
    });
    
    // Update map
    this.playerToLobby.set(playerId, lobbyId);
    
    return { lobby, lobbyId };
  }
  
  /**
   * Handle player reconnection to a lobby
   * @param {string} playerId - New ID of the reconnecting player
   * @param {string} lobbyId - ID of the lobby
   * @param {string} playerName - Name of the player
   * @returns {Object} Lobby data or error
   */
  handleReconnect(playerId, lobbyId, playerName) {
    // Check if lobby exists
    if (!this.lobbies.has(lobbyId)) {
      return { error: 'Lobby not found' };
    }
    
    const lobby = this.lobbies.get(lobbyId);
    
    // Find player by name
    const playerIndex = lobby.players.findIndex(p => p.name === playerName);
    if (playerIndex === -1) {
      return { error: 'Player not found in lobby' };
    }
    
    // Update player ID and connection status
    lobby.players[playerIndex].id = playerId;
    lobby.players[playerIndex].isConnected = true;
    
    // Update map
    this.playerToLobby.set(playerId, lobbyId);
    
    return { lobby, lobbyId };
  }
  
  /**
   * Update lobby settings
   * @param {string} playerId - ID of the host player
   * @param {Object} settings - New settings object
   * @returns {Object} Updated lobby data or error
   */
  updateSettings(playerId, settings) {
    const lobbyId = this.playerToLobby.get(playerId);
    if (!lobbyId) {
      return { error: 'Player not in a lobby' };
    }
    
    const lobby = this.lobbies.get(lobbyId);
    
    // Check if player is the host
    if (lobby.hostId !== playerId) {
      return { error: 'Only the host can update settings' };
    }
    
    // Validate settings
    if (settings.songsPerPlayer < 1 || settings.songsPerPlayer > 10) {
      return { error: 'Songs per player must be between 1 and 10' };
    }
    if (settings.maxPlayers < 3 || settings.maxPlayers > 20) {
      return { error: 'Max players must be between 3 and 20' };
    }
    
    // Update settings
    lobby.settings = {
      ...lobby.settings,
      ...settings
    };
    
    return { lobby, lobbyId };
  }
  
  /**
   * Start the game from lobby state
   * @param {string} playerId - ID of the host player
   * @returns {Object} Updated lobby data or error
   */
  startGame(playerId) {
    const lobbyId = this.playerToLobby.get(playerId);
    if (!lobbyId) {
      return { error: 'Player not in a lobby' };
    }
    
    const lobby = this.lobbies.get(lobbyId);
    
    // Check if player is the host
    if (lobby.hostId !== playerId) {
      return { error: 'Only the host can start the game' };
    }
    
    // Check if in correct state
    if (lobby.state !== 'LOBBY') {
      return { error: 'Game already started' };
    }
    
    // Check if enough players
    if (lobby.players.length < lobby.settings.minPlayers) {
      return { error: `Need at least ${lobby.settings.minPlayers} players to start` };
    }
    
    // Move to song submission state
    lobby.state = 'SONG_SUBMISSION';
    
    return { lobby, lobbyId };
  }
  
  /**
   * Add a song for a player
   * @param {string} playerId - ID of the player
   * @param {Object} song - Song data (title, artist, youtubeLink)
   * @returns {Object} Updated lobby data or error
   */
  async addSong(playerId, song) {
    console.log(`[DEBUG] addSong called for player ID: ${playerId}`);
    
    // Check if player is in a lobby
    let lobbyId = this.playerToLobby.get(playerId);
    if (!lobbyId) {
      // If no mapping exists, try to find the player in any lobby
      console.log(`[DEBUG] No lobby mapping found for player ID: ${playerId}, trying to locate player...`);
      
      // Search all lobbies for this player
      let foundLobby = null;
      let foundPlayerIndex = -1;
      
      for (const [id, lobby] of this.lobbies.entries()) {
        const playerIndex = lobby.players.findIndex(p => p.id === playerId);
        if (playerIndex !== -1) {
          foundLobby = lobby;
          lobbyId = id;
          foundPlayerIndex = playerIndex;
          console.log(`[DEBUG] Found player in lobby ${id} at index ${playerIndex}`);
          
          // Fix the mapping for future requests
          this.playerToLobby.set(playerId, id);
          break;
        }
      }
      
      if (!foundLobby) {
        console.log(`[DEBUG] Player not found in any lobby. Available lobbies: ${Array.from(this.lobbies.keys())}`);
        return { error: 'Player not in a lobby' };
      }
    }
    
    const lobby = this.lobbies.get(lobbyId);
    if (!lobby) {
      console.log(`[DEBUG] Lobby ID ${lobbyId} not found`);
      return { error: 'Lobby not found' };
    }
    
    // Log lobby state for debugging
    console.log(`[DEBUG] Lobby ${lobbyId} state: ${lobby.state}`);
    console.log(`[DEBUG] Lobby players: ${JSON.stringify(lobby.players.map(p => ({id: p.id, name: p.name})))}`);
    
    // Check if in correct state
    if (lobby.state !== 'SONG_SUBMISSION') {
      return { error: 'Cannot add songs at this time' };
    }
    
    // Get player
    const playerIndex = lobby.players.findIndex(p => p.id === playerId);
    if (playerIndex === -1) {
      console.log(`[DEBUG] Player ID ${playerId} not found in lobby ${lobbyId}.`);
      
      // First try: Find the host if this is a host request
      if (playerId === lobby.hostId || lobby.players.some(p => p.id === lobby.hostId)) {
        console.log('[DEBUG] This appears to be the host. Looking for host player...');
        const hostIndex = lobby.players.findIndex(p => p.id === lobby.hostId);
        if (hostIndex !== -1) {
          console.log(`[DEBUG] Found host at index ${hostIndex}`);
          return this._addSongToPlayer(hostIndex, lobby, lobbyId, song, playerId);
        }
      }
      
      // Second try: Match any connected player as fallback
      console.log('[DEBUG] Trying to find any connected player...');
      const connectedIndex = lobby.players.findIndex(p => p.isConnected);
      if (connectedIndex !== -1) {
        console.log(`[DEBUG] Found connected player at index ${connectedIndex}`);
        return this._addSongToPlayer(connectedIndex, lobby, lobbyId, song, playerId);
      }
      
      // If we still can't find a match, use the first player as last resort
      if (lobby.players.length > 0) {
        console.log('[DEBUG] Using first player as last resort');
        return this._addSongToPlayer(0, lobby, lobbyId, song, playerId);
      }
      
      return { error: 'Player not found in lobby' };
    }
    
    // Check if player can add more songs
    if (lobby.players[playerIndex].songs.length >= lobby.settings.songsPerPlayer) {
      return { error: 'Maximum number of songs reached' };
    }
    
    // We only require the YouTube link now
    if (!song.youtubeLink) {
      return { error: 'YouTube link is required' };
    }
    
    // If the YouTube link isn't valid, return an error
    const videoId = await youtubeAPI.extractVideoId(song.youtubeLink);
    if (!videoId) {
      return { error: 'Invalid YouTube link' };
    }
    
    // Handle async metadata fetching
    return this._addSongToPlayer(playerIndex, lobby, lobbyId, song, playerId);
  }
  
  /**
   * Search for songs on YouTube
   * @param {string} query - Search query
   * @returns {Promise<Array>} Search results
   */
  async searchYouTube(query) {
    try {
      return await youtubeAPI.searchYouTube(query);
    } catch (error) {
      console.error('Error searching YouTube:', error);
      return { error: 'Failed to search YouTube' };
    }
  }
  
  /**
   * Helper method to add a song to a player
   * @param {number} playerIndex - Index of the player in the lobby
   * @param {Object} lobby - The lobby object
   * @param {string} lobbyId - ID of the lobby
   * @param {Object} song - Song data to add
   * @param {string} playerId - ID of the player adding the song
   * @returns {Object|Promise<Object>} Updated lobby data
   * @private
   */
  async _addSongToPlayer(playerIndex, lobby, lobbyId, song, playerId) {
    // Generate song ID
    const songId = uuidv4();
    
    // Prepare song data
    let title = song.title;
    let artist = song.artist;
    let thumbnail = song.thumbnail || null; // Use client-provided thumbnail if available
    
    // If YouTube link is provided but title/artist are empty, try to fetch metadata
    if (song.youtubeLink && (!title || !artist || title === 'Unknown' || artist === 'Unknown' || !thumbnail)) {
      try {
        // Extract video ID from YouTube link
        const videoId = youtubeAPI.extractVideoId(song.youtubeLink);
        
        if (videoId) {
          console.log(`Getting metadata for YouTube video: ${videoId}`);
          // Fetch metadata from YouTube API
          const metadata = await youtubeAPI.getVideoMetadata(videoId);
          
          // Only update if we don't have values or they're generic
          if (!title || title === 'Unknown') title = metadata.title || 'Unknown';
          if (!artist || artist === 'Unknown') artist = metadata.artist || 'Unknown';
          if (!thumbnail) thumbnail = metadata.thumbnail;
          
          console.log(`Fetched metadata: "${title}" by ${artist}`);
        }
      } catch (error) {
        console.error('Error fetching YouTube metadata:', error.message);
        // Continue with user-provided data if API fails
      }
    }
    
    // Add song to player and global song list
    const newSong = {
      id: songId,
      title: title,
      artist: artist,
      youtubeLink: song.youtubeLink || '',
      thumbnail: thumbnail,
      submittedById: playerId,
      submittedByName: lobby.players[playerIndex].name
    };
    
    // Add song to both player's list and global list
    console.log(`[DEBUG] Adding song "${title}" to player at index ${playerIndex} in lobby ${lobbyId}`);
    lobby.players[playerIndex].songs.push(newSong);
    lobby.players[playerIndex].songCount = lobby.players[playerIndex].songs.length;
    lobby.songs.push(newSong);
    
    console.log(`[DEBUG] Updated player song count: ${lobby.players[playerIndex].songCount}`);
    console.log(`[DEBUG] Total songs in lobby: ${lobby.songs.length}`);
    
    // Make sure we're returning a properly formed result with both lobby and lobbyId
    const result = { lobby, lobbyId };
    
    // Validate that the result contains what we expect before returning
    if (!result.lobby || !result.lobbyId) {
      console.log(`[DEBUG] CRITICAL ERROR: Result is missing lobby or lobbyId after adding song`);
      console.log(`[DEBUG] Result keys: ${Object.keys(result)}`);
    } else {
      console.log(`[DEBUG] Song successfully added, returning result with valid lobby and lobbyId`);
    }
    
    return result;
  }
  
  /**
   * Remove a song for a player
   * @param {string} playerId - ID of the player
   * @param {string} songId - ID of the song to remove
   * @returns {Object} Updated lobby data or error
   */
  removeSong(playerId, songId) {
    const lobbyId = this.playerToLobby.get(playerId);
    if (!lobbyId) {
      return { error: 'Player not in a lobby' };
    }
    
    const lobby = this.lobbies.get(lobbyId);
    
    // Check if in correct state
    if (lobby.state !== 'SONG_SUBMISSION') {
      return { error: 'Cannot remove songs at this time' };
    }
    
    // Get player
    const playerIndex = lobby.players.findIndex(p => p.id === playerId);
    if (playerIndex === -1) {
      return { error: 'Player not found in lobby' };
    }
    
    // Find song in player's songs
    const songIndex = lobby.players[playerIndex].songs.findIndex(s => s.id === songId);
    if (songIndex === -1) {
      return { error: 'Song not found' };
    }
    
    // Remove song from player's list
    lobby.players[playerIndex].songs.splice(songIndex, 1);
    lobby.players[playerIndex].songCount = lobby.players[playerIndex].songs.length;
    
    // Remove from global song list
    const globalSongIndex = lobby.songs.findIndex(s => s.id === songId);
    if (globalSongIndex !== -1) {
      lobby.songs.splice(globalSongIndex, 1);
    }
    
    // If player was ready, set to not ready
    if (lobby.players[playerIndex].isReady) {
      lobby.players[playerIndex].isReady = false;
    }
    
    return { lobby, lobbyId };
  }
  
  /**
   * Set player ready status in song submission phase
   * @param {string} playerId - ID of the player
   * @returns {Object} Updated lobby data or error
   */
  setPlayerReady(playerId) {
    const lobbyId = this.playerToLobby.get(playerId);
    if (!lobbyId) {
      return { error: 'Player not in a lobby' };
    }
    
    const lobby = this.lobbies.get(lobbyId);
    
    // Check if in correct state
    if (lobby.state !== 'SONG_SUBMISSION') {
      return { error: 'Cannot set ready status at this time' };
    }
    
    // Get player
    const playerIndex = lobby.players.findIndex(p => p.id === playerId);
    if (playerIndex === -1) {
      return { error: 'Player not found in lobby' };
    }
    
    // Check if player has submitted enough songs
    if (lobby.players[playerIndex].songs.length < lobby.settings.songsPerPlayer) {
      return { error: 'Must submit all songs before ready' };
    }
    
    // Set player as ready
    lobby.players[playerIndex].isReady = true;
    
    // Check if all players are ready
    const allReady = lobby.players.every(p => p.isReady || !p.isConnected);
    if (allReady) {
      console.log('All players ready, starting tournament...');
      // Start the tournament by creating brackets
      this._startTournament(lobby);
      
      // Return an indicator that the tournament has started
      return { 
        lobby, 
        lobbyId,
        tournamentStarted: true
      };
    }
    
    return { lobby, lobbyId };
  }
  
  /**
   * Submit a vote for a song in a battle
   * @param {string} playerId - ID of the voting player
   * @param {string} songId - ID of the voted song
   * @returns {Object} Updated lobby data or error
   */
  submitVote(playerId, songId) {
    const lobbyId = this.playerToLobby.get(playerId);
    if (!lobbyId) {
      return { error: 'Player not in a lobby' };
    }
    
    const lobby = this.lobbies.get(lobbyId);
    
    // Check if in correct state
    if (lobby.state !== 'VOTING') {
      return { error: 'Cannot vote at this time' };
    }
    
    // Check if there's an active battle
    if (!lobby.currentBattle) {
      return { error: 'No active battle' };
    }
    
    // Check if player has already voted in this battle
    if (lobby.currentBattle.votes.has(playerId)) {
      return { error: 'Already voted in this battle' };
    }
    
    // Check if the voted song is part of the current battle
    if (songId !== lobby.currentBattle.song1.id && songId !== lobby.currentBattle.song2.id) {
      return { error: 'Invalid song ID' };
    }
    
    // Record the vote
    lobby.currentBattle.votes.set(playerId, songId);
    
    // Update vote counts
    if (songId === lobby.currentBattle.song1.id) {
      lobby.currentBattle.song1Votes++;
    } else {
      lobby.currentBattle.song2Votes++;
    }
    
    // Check if all connected players have voted
    const connectedPlayers = lobby.players.filter(p => p.isConnected).length;
    const voteCount = lobby.currentBattle.votes.size;
    
    if (voteCount >= connectedPlayers) {
      // Determine winner
      const winnerSongId = lobby.currentBattle.song1Votes > lobby.currentBattle.song2Votes
        ? lobby.currentBattle.song1.id
        : lobby.currentBattle.song2.id;
      
      lobby.currentBattle.winner = winnerSongId;
      
      // Save battle to history
      lobby.battles.push({
        round: lobby.currentBattle.round,
        song1: lobby.currentBattle.song1,
        song2: lobby.currentBattle.song2,
        song1Votes: lobby.currentBattle.song1Votes,
        song2Votes: lobby.currentBattle.song2Votes,
        winner: winnerSongId
      });
      
      // Move to next battle or finish tournament
      this._moveToNextBattle(lobby);
    }
    
    return { lobby, lobbyId };
  }
  
  /**
   * Handle player leaving a lobby
   * @param {string} playerId - ID of the player
   * @returns {Object} Updated lobby data or null if lobby is removed
   */
  leaveLobby(playerId) {
    const lobbyId = this.playerToLobby.get(playerId);
    if (!lobbyId) {
      return null; // Player not in a lobby
    }
    
    const lobby = this.lobbies.get(lobbyId);
    
    // Remove player from lobby
    const playerIndex = lobby.players.findIndex(p => p.id === playerId);
    if (playerIndex !== -1) {
      // If game hasn't started, remove player completely
      if (lobby.state === 'LOBBY') {
        lobby.players.splice(playerIndex, 1);
      } else {
        // Otherwise just mark as disconnected
        lobby.players[playerIndex].isConnected = false;
      }
    }
    
    // Remove from map
    this.playerToLobby.delete(playerId);
    
    // If it's the host leaving and game hasn't started, assign new host or delete lobby
    if (lobby.hostId === playerId && lobby.state === 'LOBBY') {
      if (lobby.players.length > 0) {
        lobby.hostId = lobby.players[0].id;
      } else {
        this.lobbies.delete(lobbyId);
        return null;
      }
    }
    
    // If all players have left, remove the lobby
    const connectedPlayers = lobby.players.filter(p => p.isConnected).length;
    if (connectedPlayers === 0) {
      this.lobbies.delete(lobbyId);
      return null;
    }
    
    // For voting state, check if we need to progress
    if (lobby.state === 'VOTING' && lobby.currentBattle) {
      // Add null check for votes property
      const totalVotes = lobby.currentBattle.votes?.size || 0;
      const remainingPlayers = lobby.players.filter(p => p.isConnected).length;
      
      if (totalVotes >= remainingPlayers && totalVotes > 0) {
        // Determine winner
        const winnerSongId = lobby.currentBattle.song1Votes > lobby.currentBattle.song2Votes
          ? lobby.currentBattle.song1.id
          : lobby.currentBattle.song2.id;
        
        lobby.currentBattle.winner = winnerSongId;
        
        // Save battle to history
        lobby.battles.push({
          round: lobby.currentBattle.round,
          song1: lobby.currentBattle.song1,
          song2: lobby.currentBattle.song2,
          song1Votes: lobby.currentBattle.song1Votes,
          song2Votes: lobby.currentBattle.song2Votes,
          winner: winnerSongId
        });
        
        // Move to next battle or finish tournament
        this._moveToNextBattle(lobby);
      }
    }
    
    return { lobby, lobbyId };
  }
  
  /**
   * Handle player disconnection
   * @param {string} playerId - ID of the disconnected player
   * @returns {Object|null} Updated lobby data or null if no lobby found
   */
  handleDisconnect(playerId) {
    return this.leaveLobby(playerId);
  }
  
  /**
   * Generate a unique lobby code
   * @returns {string} 6-character lobby code
   * @private
   */
  _generateLobbyCode() {
    const characters = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // Removed ambiguous characters
    let result = '';
    
    // Generate code until unique
    do {
      result = '';
      for (let i = 0; i < 6; i++) {
        result += characters.charAt(Math.floor(Math.random() * characters.length));
      }
    } while (this.lobbies.has(result));
    
    return result;
  }
  
  /**
   * Start the tournament by creating brackets and first battle
   * @param {Object} lobby - Lobby object
   * @private
   */
  _startTournament(lobby) {
    // Collect all submitted songs
    const songs = [...lobby.songs];
    
    // Shuffle songs
    this._shuffleArray(songs);
    
    // Create tournament brackets
    const brackets = [];
    let round = 0;
    
    // Handle odd number of songs by giving a bye to one song
    if (songs.length % 2 !== 0 && songs.length > 1) {
      const byeSong = songs.pop();
      brackets.push({
        round,
        song1: byeSong,
        song2: null, // Bye
        bye: true,
        winner: byeSong.id,
        song1Votes: 0,
        song2Votes: 0
      });
    }
    
    // Create initial bracket pairs
    for (let i = 0; i < songs.length; i += 2) {
      if (i + 1 < songs.length) {
        brackets.push({
          round,
          song1: songs[i],
          song2: songs[i + 1],
          song1Votes: 0,
          song2Votes: 0,
          winner: null,
          votes: new Map()
        });
      }
    }
    
    // Store brackets and transition to voting state
    lobby.brackets = brackets;
    lobby.currentBracketIndex = 0;
    
    if (brackets.length > 0) {
      // Set the first battle
      lobby.state = 'VOTING';
      
      // Ensure we create a proper battle object with all required fields
      lobby.currentBattle = {
        ...brackets[0],
        // Make sure votes is a Map (it might be serialized incorrectly)
        votes: brackets[0].votes || new Map(),
        voteCount: 0
      };
      
      console.log('Starting first battle:', lobby.currentBattle);
    } else {
      // Edge case: only one song submitted
      if (songs.length === 1) {
        lobby.state = 'FINISHED';
        lobby.finalWinner = songs[0];
      }
    }
  }
  
  /**
   * Move to next battle or finish tournament
   * @param {Object} lobby - Lobby object
   * @private
   */
  _moveToNextBattle(lobby) {
    // Check if there are more battles in current round
    lobby.currentBracketIndex++;
    
    if (lobby.currentBracketIndex < lobby.brackets.length) {
      // Move to next battle
      lobby.currentBattle = lobby.brackets[lobby.currentBracketIndex];
      return;
    }
    
    // Current round complete, check if tournament is finished
    const winners = lobby.brackets
      .filter(b => !b.bye) // Skip byes
      .map(b => {
        const winningSong = b.song1.id === b.winner ? b.song1 : b.song2;
        return winningSong;
      });
    
    // Add byes to winners
    const byes = lobby.brackets
      .filter(b => b.bye)
      .map(b => b.song1);
    
    const nextRoundSongs = [...winners, ...byes];
    
    // If only one song remains, we have a winner
    if (nextRoundSongs.length <= 1) {
      lobby.state = 'FINISHED';
      lobby.finalWinner = nextRoundSongs[0];
      lobby.currentBattle = null;
      return;
    }
    
    // Create brackets for next round
    const nextRound = [];
    const round = lobby.brackets[0].round + 1;
    
    // Handle odd number of songs by giving a bye to one song
    if (nextRoundSongs.length % 2 !== 0) {
      const byeSong = nextRoundSongs.pop();
      nextRound.push({
        round,
        song1: byeSong,
        song2: null,
        bye: true,
        winner: byeSong.id,
        song1Votes: 0,
        song2Votes: 0
      });
    }
    
    // Create pairs for next round
    for (let i = 0; i < nextRoundSongs.length; i += 2) {
      if (i + 1 < nextRoundSongs.length) {
        nextRound.push({
          round,
          song1: nextRoundSongs[i],
          song2: nextRoundSongs[i + 1],
          song1Votes: 0,
          song2Votes: 0,
          winner: null,
          votes: new Map()
        });
      }
    }
    
    // Update brackets and reset index
    lobby.brackets = nextRound;
    lobby.currentBracketIndex = 0;
    
    // Set first battle of new round
    if (nextRound.length > 0) {
      lobby.currentBattle = nextRound[0];
    }
  }
  
  /**
   * Shuffle array in-place using Fisher-Yates algorithm
   * @param {Array} array - Array to shuffle
   * @private
   */
  _shuffleArray(array) {
    for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [array[i], array[j]] = [array[j], array[i]];
    }
  }
}

// Export GameManager class
module.exports = GameManager;