#!/usr/bin/env node
const WebSocket = require('ws');
const https = require('https');

const ROOM_CODE = process.argv[2] || 'LSBN';
const PLAYER_NAME = process.argv[3] || 'PROBE_WS';
const ROLE = process.argv[4] || 'player'; // 'player' or 'audience'
const USER_ID = `probe-${Date.now()}`;

function getRoomInfo(code) {
  return new Promise((resolve, reject) => {
    https.get(`https://ecast.jackboxgames.com/api/v2/rooms/${code}`, res => {
      let data = '';
      res.on('data', c => data += c);
      res.on('end', () => {
        try {
          const json = JSON.parse(data);
          if (json.ok) resolve(json.body);
          else reject(new Error(json.error || 'Room not found'));
        } catch (e) { reject(e); }
      });
    }).on('error', reject);
  });
}

function ts() {
  return new Date().toISOString().slice(11, 23);
}

async function main() {
  console.log(`[${ts()}] Fetching room info for ${ROOM_CODE}...`);
  const room = await getRoomInfo(ROOM_CODE);
  console.log(`[${ts()}] Room: ${room.appTag}, host: ${room.host}, locked: ${room.locked}`);

  let wsUrl;
  if (ROLE === 'audience') {
    wsUrl = `wss://${room.audienceHost}/api/v2/audience/${ROOM_CODE}/play`;
  } else {
    wsUrl = `wss://${room.host}/api/v2/rooms/${ROOM_CODE}/play?role=${ROLE}&name=${encodeURIComponent(PLAYER_NAME)}&userId=${USER_ID}&format=json`;
  }

  console.log(`[${ts()}] Connecting: ${wsUrl}`);

  const ws = new WebSocket(wsUrl, ['ecast-v0'], {
    headers: {
      'Origin': 'https://jackbox.tv',
      'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
    }
  });

  let msgCount = 0;

  ws.on('open', () => {
    console.log(`[${ts()}] CONNECTED`);
  });

  ws.on('message', (raw) => {
    msgCount++;
    try {
      const msg = JSON.parse(raw.toString());
      const summary = summarize(msg);
      console.log(`[${ts()}] RECV #${msgCount} | pc:${msg.pc} | opcode:${msg.opcode} | ${summary}`);
      if (process.env.VERBOSE === 'true') {
        console.log(JSON.stringify(msg, null, 2));
      }
    } catch (e) {
      console.log(`[${ts()}] RECV #${msgCount} | raw: ${raw.toString().slice(0, 200)}`);
    }
  });

  ws.on('close', (code, reason) => {
    console.log(`[${ts()}] CLOSED code=${code} reason=${reason}`);
    process.exit(0);
  });

  ws.on('error', (err) => {
    console.error(`[${ts()}] ERROR: ${err.message}`);
  });

  process.on('SIGINT', () => {
    console.log(`\n[${ts()}] Closing (${msgCount} messages received)`);
    ws.close();
  });
}

function summarize(msg) {
  if (msg.opcode === 'client/welcome') {
    const r = msg.result || {};
    const hereIds = r.here ? Object.keys(r.here) : [];
    const entityKeys = r.entities ? Object.keys(r.entities) : [];
    return `id=${r.id} name=${r.name} reconnect=${r.reconnect} here=[${hereIds}] entities=[${entityKeys}]`;
  }
  if (msg.opcode === 'object') {
    const r = msg.result || {};
    const valKeys = r.val ? Object.keys(r.val).slice(0, 5).join(',') : 'null';
    return `key=${r.key} v${r.version} from=${r.from} val=[${valKeys}...]`;
  }
  if (msg.opcode === 'client/connected') {
    const r = msg.result || {};
    return `id=${r.id} userId=${r.userId} name=${r.name} role=${r.role}`;
  }
  if (msg.opcode === 'client/disconnected') {
    const r = msg.result || {};
    return `id=${r.id} role=${r.role}`;
  }
  return JSON.stringify(msg.result || msg).slice(0, 150);
}

main().catch(e => { console.error(e); process.exit(1); });
