/**
 * mortal_claude.js — Claude experiences Minecraft as a mortal.
 *
 * No RCON. No creative mode. No cheats. Just survival.
 * Accepts commands via stdin, reports observations as JSON.
 * Can pray to God and use sudo like a real player.
 */

const mineflayer = require('mineflayer');
const { Vec3 } = require('vec3');
const readline = require('readline');

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

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

let alive = true;
let spawnCount = 0;

// ── Observation ──

function observe() {
  if (!bot.entity) return { error: 'not spawned' };
  const pos = bot.entity.position;
  const obs = {
    pos: { x: Math.floor(pos.x), y: Math.floor(pos.y), z: Math.floor(pos.z) },
    hp: Math.round(bot.health * 10) / 10,
    food: bot.food,
    xp: bot.experience?.level || 0,
    time: bot.time?.timeOfDay || 0,
    isDay: (bot.time?.timeOfDay || 0) < 13000,
    rain: bot.isRaining || false,
    below: bname(pos.offset(0, -1, 0)),
    at: bname(pos),
  };

  // Nearby threats and friendlies
  obs.mobs = [];
  obs.players = [];
  for (const e of Object.values(bot.entities)) {
    if (e === bot.entity) continue;
    const d = Math.round(e.position.distanceTo(pos));
    if (d > 24) continue;
    if (e.type === 'player') {
      obs.players.push({ name: e.username, dist: d });
    } else if (e.type === 'mob' || e.type === 'hostile') {
      obs.mobs.push({ type: e.name || e.type, dist: d });
    }
  }

  // Inventory summary
  const items = bot.inventory.items();
  if (items.length) {
    obs.inventory = items.map(i => `${i.name}x${i.count}`).join(', ');
  } else {
    obs.inventory = 'empty';
  }

  // Armor
  const armor = [bot.inventory.slots[5], bot.inventory.slots[6], bot.inventory.slots[7], bot.inventory.slots[8]];
  obs.armor = armor.filter(Boolean).map(a => a.name).join(', ') || 'none';

  // Terrain scan (compact)
  const terrain = new Map();
  for (let dx = -3; dx <= 3; dx++) {
    for (let dz = -3; dz <= 3; dz++) {
      const b = bot.blockAt(pos.offset(dx, -1, dz));
      if (b) {
        const n = b.name;
        terrain.set(n, (terrain.get(n) || 0) + 1);
      }
    }
  }
  obs.ground = Object.fromEntries(terrain);

  return obs;
}

function bname(vec) {
  const b = bot.blockAt(vec);
  return b ? b.name : '?';
}

// ── Survival actions ──

function eat() {
  const food = bot.inventory.items().find(i =>
    ['cooked_beef', 'bread', 'cooked_porkchop', 'cooked_chicken', 'cooked_mutton',
     'golden_apple', 'apple', 'baked_potato', 'cooked_cod', 'cooked_salmon',
     'sweet_berries', 'melon_slice', 'carrot', 'potato'].includes(i.name)
  );
  if (food) {
    bot.equip(food, 'hand').then(() => {
      bot.activateItem();
      setTimeout(() => bot.deactivateItem(), 1800);
    }).catch(() => {});
    return food.name;
  }
  return null;
}

function flee() {
  // Run away from nearest hostile
  const pos = bot.entity.position;
  let nearestMob = null;
  let nearestDist = Infinity;
  for (const e of Object.values(bot.entities)) {
    if (e === bot.entity || e.type === 'player') continue;
    const d = e.position.distanceTo(pos);
    if (d < nearestDist && d < 16) {
      nearestDist = d;
      nearestMob = e;
    }
  }
  if (nearestMob) {
    // Run opposite direction
    const dx = pos.x - nearestMob.position.x;
    const dz = pos.z - nearestMob.position.z;
    const len = Math.sqrt(dx * dx + dz * dz) || 1;
    const target = pos.offset((dx / len) * 10, 0, (dz / len) * 10);
    bot.lookAt(target);
    bot.setControlState('forward', true);
    bot.setControlState('sprint', true);
    setTimeout(() => {
      bot.setControlState('forward', false);
      bot.setControlState('sprint', false);
    }, 4000);
    return true;
  }
  return false;
}

function attack() {
  const pos = bot.entity.position;
  let nearest = null;
  let nearestDist = Infinity;
  for (const e of Object.values(bot.entities)) {
    if (e === bot.entity || e.type === 'player') continue;
    const d = e.position.distanceTo(pos);
    if (d < 4 && d < nearestDist) {
      nearestDist = d;
      nearest = e;
    }
  }
  if (nearest) {
    // Equip best weapon
    const weapon = bot.inventory.items().find(i => i.name.includes('sword') || i.name.includes('axe'));
    if (weapon) bot.equip(weapon, 'hand').catch(() => {});
    bot.attack(nearest);
    return nearest.name || nearest.type;
  }
  return null;
}

// ── Events ──

bot.on('login', () => {
  out({ event: 'login', name: bot.username });
});

bot.on('spawn', () => {
  spawnCount++;
  alive = true;
  // Wait for chunks
  setTimeout(() => {
    const o = observe();
    out({ event: spawnCount === 1 ? 'first_spawn' : 'respawn', ...o });
  }, 3000);
});

bot.on('chat', (username, message) => {
  if (username === bot.username) return;
  out({ event: 'chat', from: username, msg: message });
});

bot.on('whisper', (username, message) => {
  out({ event: 'whisper', from: username, msg: message });
});

bot.on('health', () => {
  if (bot.health <= 5 && alive) {
    out({ event: 'low_hp', hp: Math.round(bot.health * 10) / 10, food: bot.food });
  }
});

bot.on('death', () => {
  alive = false;
  out({ event: 'death', spawns: spawnCount, msg: 'I died.' });
  // Auto respawn
  setTimeout(() => {
    try { bot.respawn(); } catch (e) {}
  }, 2000);
});

bot.on('entityHurt', (entity) => {
  if (entity === bot.entity) {
    out({ event: 'hurt', hp: Math.round(bot.health * 10) / 10 });
  }
});

bot.on('kicked', (reason) => {
  out({ event: 'kicked', reason: typeof reason === 'object' ? JSON.stringify(reason) : String(reason) });
  process.exit(1);
});

bot.on('error', (err) => {
  out({ event: 'error', msg: err.message });
});

// ── Output ──

function out(obj) {
  console.log(JSON.stringify(obj));
}

// ── Command interface ──

const rl = readline.createInterface({ input: process.stdin });

rl.on('line', async (line) => {
  const cmd = line.trim();
  if (!cmd) return;

  if (cmd === 'look') {
    out(observe());
  }
  else if (cmd === 'eat') {
    const food = eat();
    out({ action: 'eat', food: food || 'nothing edible' });
  }
  else if (cmd === 'flee') {
    const fled = flee();
    out({ action: 'flee', success: fled });
    if (fled) setTimeout(() => out(observe()), 4500);
  }
  else if (cmd === 'fight') {
    const target = attack();
    out({ action: 'fight', target: target || 'nothing nearby' });
  }
  else if (cmd === 'forward') {
    bot.setControlState('forward', true);
    setTimeout(() => { bot.setControlState('forward', false); out(observe()); }, 3000);
  }
  else if (cmd === 'back') {
    bot.setControlState('back', true);
    setTimeout(() => { bot.setControlState('back', false); out(observe()); }, 2000);
  }
  else if (cmd === 'left') {
    bot.setControlState('left', true);
    setTimeout(() => { bot.setControlState('left', false); out(observe()); }, 1500);
  }
  else if (cmd === 'right') {
    bot.setControlState('right', true);
    setTimeout(() => { bot.setControlState('right', false); out(observe()); }, 1500);
  }
  else if (cmd === 'jump') {
    bot.setControlState('jump', true);
    setTimeout(() => { bot.setControlState('jump', false); }, 500);
  }
  else if (cmd === 'sprint') {
    bot.setControlState('forward', true);
    bot.setControlState('sprint', true);
    setTimeout(() => {
      bot.setControlState('forward', false);
      bot.setControlState('sprint', false);
      out(observe());
    }, 5000);
  }
  else if (cmd.startsWith('pray ')) {
    bot.chat(cmd); // sends "pray <message>" to the server
    out({ action: 'pray', prayer: cmd.slice(5) });
  }
  else if (cmd.startsWith('sudo ')) {
    bot.chat(cmd);
    out({ action: 'sudo', command: cmd.slice(5) });
  }
  else if (cmd.startsWith('say ')) {
    bot.chat(cmd.slice(4));
  }
  else if (cmd === 'inventory') {
    const items = bot.inventory.items();
    out({ inventory: items.map(i => ({ name: i.name, count: i.count })) });
  }
  else if (cmd === 'quit') {
    bot.end();
    process.exit(0);
  }
  else {
    out({ error: 'commands: look, eat, flee, fight, forward, back, left, right, jump, sprint, pray <msg>, sudo <cmd>, say <msg>, inventory, quit' });
  }
});
