/**
 * aware_bots.js -- Mineflayer bots that log everything they experience.
 *
 * Each bot tracks and logs:
 *   - Position changes (teleports, movement)
 *   - Health changes (damage, healing)
 *   - Chat messages
 *   - Effects applied/removed
 *   - Block changes in their vicinity
 *   - Items received
 *   - Environmental state (underwater, on fire, in air)
 *   - Deaths
 *
 * Usage: node aware_bots.js [count] [host] [port]
 * Defaults: 3 bots, 192.168.0.244:25568
 */

const mineflayer = require('mineflayer');

const count = parseInt(process.argv[2] || '3', 10);
const host = process.argv[3] || '192.168.0.244';
const port = parseInt(process.argv[4] || '25568', 10);

const bots = [];
let connected = 0;

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

function fmtPos(pos) {
  if (!pos) return '?,?,?';
  return `${pos.x.toFixed(1)},${pos.y.toFixed(1)},${pos.z.toFixed(1)}`;
}

function spawnBot(index) {
  const name = `TrainBot_${index}`;
  console.log(`[${ts()}] [${name}] Connecting to ${host}:${port}...`);

  const bot = mineflayer.createBot({
    host,
    port,
    username: name,
    auth: 'offline',
    version: '1.21.11',
    viewDistance: 'tiny',
  });

  bot._name = name;
  bots.push(bot);

  // --- State tracking ---
  let lastPos = null;
  let lastHealth = null;
  let lastFood = null;
  let lastOxygen = null;
  let isUnderwater = false;
  let isOnFire = false;

  bot.on('login', () => {
    console.log(`[${ts()}] [${name}] Logged in`);
  });

  bot.on('spawn', () => {
    connected++;
    const pos = bot.entity.position;
    lastPos = { x: pos.x, y: pos.y, z: pos.z };
    lastHealth = bot.health;
    lastFood = bot.food;
    lastOxygen = bot.oxygenLevel;
    console.log(`[${ts()}] [${name}] SPAWN at (${fmtPos(pos)}) gamemode=${bot.game.gameMode} -- ${connected}/${count}`);

    if (connected === count) {
      console.log(`[${ts()}] === All ${count} bots connected and aware ===`);
    }
  });

  // --- Position tracking (teleport detection) ---
  bot.on('forcedMove', () => {
    const pos = bot.entity.position;
    const oldStr = lastPos ? fmtPos(lastPos) : '?';
    console.log(`[${ts()}] [${name}] TELEPORTED from (${oldStr}) to (${fmtPos(pos)})`);
    lastPos = { x: pos.x, y: pos.y, z: pos.z };
  });

  // --- Health tracking ---
  bot.on('health', () => {
    const hp = bot.health;
    const food = bot.food;
    const oxy = bot.oxygenLevel;

    if (lastHealth !== null && hp !== lastHealth) {
      const delta = hp - lastHealth;
      const verb = delta < 0 ? 'DAMAGED' : 'HEALED';
      console.log(`[${ts()}] [${name}] ${verb} ${delta.toFixed(1)} (health: ${hp.toFixed(1)}/20)`);
    }

    if (lastFood !== null && food !== lastFood) {
      console.log(`[${ts()}] [${name}] FOOD changed ${lastFood} -> ${food}`);
    }

    if (lastOxygen !== null && oxy !== lastOxygen) {
      if (oxy < lastOxygen && !isUnderwater) {
        isUnderwater = true;
        console.log(`[${ts()}] [${name}] UNDERWATER (air: ${oxy})`);
      } else if (oxy > lastOxygen && isUnderwater) {
        isUnderwater = false;
        console.log(`[${ts()}] [${name}] SURFACED (air: ${oxy})`);
      }
    }

    lastHealth = hp;
    lastFood = food;
    lastOxygen = oxy;
  });

  // --- Chat ---
  bot.on('chat', (username, message) => {
    if (username === name) return;
    console.log(`[${ts()}] [${name}] CHAT <${username}> ${message}`);
  });

  // --- Whisper ---
  bot.on('whisper', (username, message) => {
    console.log(`[${ts()}] [${name}] WHISPER from ${username}: ${message}`);
  });

  // --- Death ---
  bot.on('death', () => {
    console.log(`[${ts()}] [${name}] DIED at (${fmtPos(lastPos)})`);
  });

  bot.on('respawn', () => {
    const pos = bot.entity.position;
    console.log(`[${ts()}] [${name}] RESPAWNED at (${fmtPos(pos)})`);
    lastPos = { x: pos.x, y: pos.y, z: pos.z };
  });

  // --- Effects ---
  bot.on('entityEffect', (entity, effect) => {
    if (entity === bot.entity) {
      console.log(`[${ts()}] [${name}] EFFECT APPLIED id=${effect.id} amplifier=${effect.amplifier} duration=${effect.duration}`);
    }
  });

  bot.on('entityEffectEnd', (entity, effect) => {
    if (entity === bot.entity) {
      console.log(`[${ts()}] [${name}] EFFECT ENDED id=${effect.id}`);
    }
  });

  // --- Item collection ---
  bot.on('playerCollect', (collector, collected) => {
    if (collector === bot.entity) {
      console.log(`[${ts()}] [${name}] COLLECTED item entity ${collected.id}`);
    }
  });

  // --- Fire detection ---
  bot.on('entityUpdate', (entity) => {
    if (entity === bot.entity) {
      const onFire = (entity.metadata && entity.metadata[0] && (entity.metadata[0] & 0x01)) ? true : false;
      if (onFire && !isOnFire) {
        isOnFire = true;
        console.log(`[${ts()}] [${name}] ON FIRE`);
      } else if (!onFire && isOnFire) {
        isOnFire = false;
        console.log(`[${ts()}] [${name}] FIRE OUT`);
      }
    }
  });

  // --- Periodic environment report ---
  let envInterval = setInterval(() => {
    if (!bot.entity) return;
    const pos = bot.entity.position;
    const blockBelow = bot.blockAt(pos.offset(0, -1, 0));
    const blockAt = bot.blockAt(pos);
    const blockAbove = bot.blockAt(pos.offset(0, 1, 0));

    const env = [];
    if (blockAt && blockAt.name === 'water') env.push('in_water');
    if (blockAt && blockAt.name === 'lava') env.push('in_lava');
    if (blockBelow) env.push(`standing_on:${blockBelow.name}`);
    if (blockAbove && blockAbove.name === 'water') env.push('water_above');

    // Only log if something interesting
    if (env.length > 0 && (env.includes('in_water') || env.includes('in_lava'))) {
      console.log(`[${ts()}] [${name}] ENV pos=(${fmtPos(pos)}) ${env.join(', ')} hp=${bot.health} air=${bot.oxygenLevel}`);
    }
  }, 5000);

  // --- Error/disconnect handling ---
  bot.on('error', (err) => {
    console.error(`[${ts()}] [${name}] ERROR: ${err.message}`);
  });

  bot.on('kicked', (reason) => {
    let msg = reason;
    try { msg = JSON.stringify(reason); } catch (_) {}
    console.error(`[${ts()}] [${name}] KICKED: ${msg}`);
    connected = Math.max(0, connected - 1);
    clearInterval(envInterval);
  });

  bot.on('end', (reason) => {
    console.log(`[${ts()}] [${name}] DISCONNECTED: ${reason}`);
    connected = Math.max(0, connected - 1);
    clearInterval(envInterval);
  });

  return bot;
}

// Stagger connections
for (let i = 0; i < count; i++) {
  setTimeout(() => spawnBot(i), i * 1500);
}

// Graceful shutdown
process.on('SIGINT', () => {
  console.log(`\n[${ts()}] Disconnecting all bots...`);
  for (const bot of bots) {
    try { bot.quit('Shutdown'); } catch (_) {}
  }
  setTimeout(() => process.exit(0), 2000);
});

setInterval(() => {}, 60000);
