#!/usr/bin/env node

/**
 * Jackbox Player Count Fetcher
 * 
 * This script connects to a Jackbox game room and retrieves the actual player count
 * by establishing a WebSocket connection and listening for game state updates.
 * 
 * Usage:
 *   node get-jackbox-player-count.js <ROOM_CODE>
 *   node get-jackbox-player-count.js JYET
 */

const https = require('https');

// Try to load ws from multiple locations
let WebSocket;
try {
  WebSocket = require('ws');
} catch (e) {
  try {
    WebSocket = require('../backend/node_modules/ws');
  } catch (e2) {
    console.error('Error: WebSocket library (ws) not found.');
    console.error('Please run: npm install ws');
    console.error('Or run this script from the backend directory where ws is already installed.');
    process.exit(1);
  }
}

// ANSI color codes for pretty output
const colors = {
  reset: '\x1b[0m',
  bright: '\x1b[1m',
  green: '\x1b[32m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  red: '\x1b[31m',
  cyan: '\x1b[36m'
};

/**
 * Fetches room information from the Jackbox REST API
 */
async function getRoomInfo(roomCode) {
  return new Promise((resolve, reject) => {
    const url = `https://ecast.jackboxgames.com/api/v2/rooms/${roomCode}`;
    
    https.get(url, (res) => {
      let data = '';
      
      res.on('data', (chunk) => {
        data += chunk;
      });
      
      res.on('end', () => {
        try {
          const json = JSON.parse(data);
          if (json.ok) {
            resolve(json.body);
          } else {
            reject(new Error('Room not found or invalid'));
          }
        } catch (e) {
          reject(e);
        }
      });
    }).on('error', reject);
  });
}

/**
 * Connects to the Jackbox WebSocket and retrieves player count
 * Note: Direct WebSocket connection requires proper authentication flow.
 * This uses the ecast endpoint which is designed for external connections.
 */
async function getPlayerCount(roomCode, roomInfo) {
  return new Promise((resolve, reject) => {
    // Use the audienceHost (ecast) instead of direct game host
    const wsUrl = `wss://${roomInfo.audienceHost}/api/v2/audience/${roomCode}/play`;
    
    console.log(`${colors.blue}Connecting to WebSocket...${colors.reset}`);
    console.log(`${colors.cyan}URL: ${wsUrl}${colors.reset}\n`);
    
    const ws = new WebSocket(wsUrl, {
      headers: {
        'Origin': 'https://jackbox.tv',
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
      }
    });
    
    let timeout = setTimeout(() => {
      ws.close();
      reject(new Error('Connection timeout - room may be closed or unreachable'));
    }, 15000); // 15 second timeout
    
    let receivedAnyData = false;
    
    ws.on('open', () => {
      console.log(`${colors.green}✓ WebSocket connected${colors.reset}\n`);
      
      // For audience endpoint, we might not need to send join message
      // Just listen for messages
    });
    
    ws.on('message', (data) => {
      receivedAnyData = true;
      try {
        const message = JSON.parse(data.toString());
        console.log(`${colors.yellow}Received message:${colors.reset}`, message.opcode || 'unknown');
        
        // Look for various message types that might contain player info
        if (message.opcode === 'client/welcome' && message.result) {
          clearTimeout(timeout);
          
          const here = message.result.here || {};
          const playerCount = Object.keys(here).length;
          const audienceCount = message.result.entities?.audience?.[1]?.count || 0;
          const lobbyState = message.result.entities?.['bc:room']?.[1]?.val?.lobbyState || 'Unknown';
          const gameState = message.result.entities?.['bc:room']?.[1]?.val?.state || 'Unknown';
          
          // Extract player details
          const players = [];
          for (const [id, playerData] of Object.entries(here)) {
            const roles = playerData.roles || {};
            if (roles.host) {
              players.push({ id, role: 'host', name: 'Host' });
            } else if (roles.player) {
              players.push({ id, role: 'player', name: roles.player.name || 'Unknown' });
            }
          }
          
          const result = {
            roomCode,
            appTag: roomInfo.appTag,
            playerCount,
            audienceCount,
            maxPlayers: roomInfo.maxPlayers,
            gameState,
            lobbyState,
            locked: roomInfo.locked,
            full: roomInfo.full,
            players
          };
          
          ws.close();
          resolve(result);
        } else if (message.opcode === 'room/count' || message.opcode === 'audience/count-group') {
          // Audience count updates
          console.log(`${colors.cyan}Audience count message received${colors.reset}`);
        }
      } catch (e) {
        // Ignore parse errors, might be non-JSON messages
        console.log(`${colors.yellow}Parse error:${colors.reset}`, e.message);
      }
    });
    
    ws.on('error', (error) => {
      clearTimeout(timeout);
      reject(new Error(`WebSocket error: ${error.message}\n\n` +
        `${colors.yellow}Note:${colors.reset} Direct WebSocket access requires joining through jackbox.tv.\n` +
        `This limitation means we cannot directly query player count without joining the game.`));
    });
    
    ws.on('close', () => {
      clearTimeout(timeout);
      if (!receivedAnyData) {
        reject(new Error('WebSocket closed without receiving data.\n\n' +
          `${colors.yellow}Note:${colors.reset} The Jackbox WebSocket API requires authentication that's only\n` +
          `available when joining through the official jackbox.tv interface.\n\n` +
          `${colors.cyan}Alternative:${colors.reset} Use the REST API to check if room is full, or join\n` +
          `through jackbox.tv in a browser to get real-time player counts.`));
      }
    });
  });
}

/**
 * Pretty prints the results
 */
function printResults(result) {
  console.log(`${colors.bright}═══════════════════════════════════════════${colors.reset}`);
  console.log(`${colors.bright}  Jackbox Room Status${colors.reset}`);
  console.log(`${colors.bright}═══════════════════════════════════════════${colors.reset}\n`);
  
  console.log(`${colors.cyan}Room Code:${colors.reset}      ${result.roomCode}`);
  console.log(`${colors.cyan}Game:${colors.reset}           ${result.appTag}`);
  console.log(`${colors.cyan}Game State:${colors.reset}     ${result.gameState}`);
  console.log(`${colors.cyan}Lobby State:${colors.reset}    ${result.lobbyState}`);
  console.log(`${colors.cyan}Locked:${colors.reset}         ${result.locked ? 'Yes' : 'No'}`);
  console.log(`${colors.cyan}Full:${colors.reset}           ${result.full ? 'Yes' : 'No'}`);
  console.log();
  
  console.log(`${colors.bright}${colors.green}Players:${colors.reset}        ${colors.bright}${result.playerCount}${colors.reset} / ${result.maxPlayers}`);
  console.log(`${colors.cyan}Audience:${colors.reset}       ${result.audienceCount}`);
  console.log();
  
  if (result.players.length > 0) {
    console.log(`${colors.bright}Current Players:${colors.reset}`);
    result.players.forEach((player, idx) => {
      const roleColor = player.role === 'host' ? colors.yellow : colors.green;
      console.log(`  ${idx + 1}. ${roleColor}${player.name}${colors.reset} (${player.role})`);
    });
  }
  
  console.log(`\n${colors.bright}═══════════════════════════════════════════${colors.reset}\n`);
}

/**
 * Main function
 */
async function main() {
  const args = process.argv.slice(2);
  
  if (args.length === 0) {
    console.error(`${colors.red}Error: Room code required${colors.reset}`);
    console.log(`\nUsage: node get-jackbox-player-count.js <ROOM_CODE>`);
    console.log(`Example: node get-jackbox-player-count.js JYET\n`);
    process.exit(1);
  }
  
  const roomCode = args[0].toUpperCase();
  
  console.log(`${colors.bright}Jackbox Player Count Fetcher${colors.reset}`);
  console.log(`${colors.cyan}Room Code: ${roomCode}${colors.reset}\n`);
  
  try {
    // Step 1: Get room info from REST API
    console.log(`${colors.blue}Fetching room information...${colors.reset}`);
    const roomInfo = await getRoomInfo(roomCode);
    console.log(`${colors.green}✓ Room found: ${roomInfo.appTag}${colors.reset}`);
    console.log(`${colors.cyan}  Max Players: ${roomInfo.maxPlayers}${colors.reset}\n`);
    
    // Step 2: Connect to WebSocket and get player count
    const result = await getPlayerCount(roomCode, roomInfo);
    
    // Step 3: Print results
    printResults(result);
    
    // Return just the player count for scripting purposes
    if (process.env.JSON_OUTPUT === 'true') {
      console.log(JSON.stringify(result, null, 2));
    }
    
    process.exit(0);
  } catch (error) {
    console.error(`${colors.red}Error: ${error.message}${colors.reset}\n`);
    process.exit(1);
  }
}

// Run if executed directly
if (require.main === module) {
  main();
}

// Export for use as a module
module.exports = {
  getRoomInfo,
  getPlayerCount
};

