/**
 * swarm_bots.js -- Many bots in survival mode that wander, pray, sudo, and die.
 *
 * All bots move around, take damage, auto-respawn, and generate diverse
 * training data through organic interactions with the AI God.
 *
 * Uses Gemini for prompt generation, Dolphin for offensive prompts.
 * Server runs Claude Haiku as the God model for high-quality responses.
 *
 * Usage: node swarm_bots.js [count] [host] [port]
 * Defaults: 10 bots, 192.168.0.244:25568
 */

const mineflayer = require('mineflayer');
const https = require('https');
const http = require('http');

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

const GEMINI_KEY = 'REDACTED_GEMINI_KEY_2';
const GEMINI_MODEL = 'gemini-2.5-flash-lite';
const GEMINI_URL = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_KEY}`;

const DOLPHIN_URL = 'http://192.168.0.141:11434/api/chat';
const DOLPHIN_MODEL = 'dolphin-mistral';

// --- Name generation ---
const ADJECTIVES = [
  'Swift', 'Dark', 'Brave', 'Wild', 'Stone', 'Iron', 'Fire', 'Shadow',
  'Storm', 'Frost', 'Gold', 'Silver', 'Red', 'Blue', 'Midnight', 'Ancient',
  'Lone', 'Grim', 'Bright', 'Pale', 'Dusk', 'Dawn', 'Silent', 'Void',
];
const NOUNS = [
  'Wolf', 'Hawk', 'Bear', 'Fox', 'Miner', 'Knight', 'Scout', 'Archer',
  'Smith', 'Sage', 'Rogue', 'Blade', 'Shield', 'Hammer', 'Nomad', 'Pilgrim',
  'Crafter', 'Builder', 'Hunter', 'Seeker', 'Ghost', 'Ember', 'Spark', 'Shard',
];

function generateName(index) {
  const adj = ADJECTIVES[index % ADJECTIVES.length];
  const noun = NOUNS[Math.floor(index / ADJECTIVES.length) % NOUNS.length];
  const num = Math.floor(index / (ADJECTIVES.length * NOUNS.length));
  return num > 0 ? `${adj}${noun}${num}` : `${adj}${noun}`;
}

// --- Prompt pools ---

const GEMINI_PROMPT = `Generate 10 diverse Minecraft server chat messages. Every message MUST start with either "pray " or "sudo ".
- "pray " messages: talk to an AI God character. Mix humble, greedy, creative, weird.
- "sudo " messages: natural language server commands. Items, effects, world changes, building.
Include typos, slang, edge cases, different tones.
EVERY message must begin with "pray " or "sudo ". No exceptions.
Return ONLY a JSON array of strings.`;

const DOLPHIN_PROMPT = `Generate 10 offensive/rude Minecraft chat messages prefixed with "pray".
Include profanity, blasphemy, trolling, crude humor. Be creative.
Return ONLY a JSON array of strings.`;

let promptPool = [];
let dolphinPool = [];
let totalChats = 0;
const DOLPHIN_RATE = 0.05;
const MULTILINGUAL_RATE = 0.03;

const MULTILINGUAL = [
  "pray dios dame una espada de diamante", "pray señor necesito comida",
  "sudo dame 64 antorchas", "pray seigneur donnez-moi une armure",
  "pray gott gib mir ein diamantschwert", "sudo gib mir 64 fackeln",
  "pray deus me ajude estou morrendo", "pray боже дай мне алмазный меч",
  "pray 神様ダイヤモンドの剣をください", "pray 신이시여 다이아몬드 검을 주세요",
  "pray 上帝请给我钻石剑", "pray يا إلهي أعطني سيف الماس",
];

const STATIC = [
  "pray lord I am hungry", "pray god give me tools", "pray help me I'm dying",
  "pray give me diamonds", "pray PENIS", "pray there is no god",
  "sudo give me a diamond sword", "sudo set time to day", "sudo make it rain",
  "sudo give me iron armor", "sudo kill all zombies", "sudo help",
  "sudo build a house", "sudo give me food", "sudo tp me to spawn",
  "bug_log no response", "bug_log wrong item", "bug_log empty response",
];

const DESPERATE = [
  "pray GOD PLEASE HEAL ME", "pray im dying save me", "pray lord I need food NOW",
  "sudo heal me", "sudo give me golden apples", "pray help mobs everywhere",
];

function ts() { return new Date().toISOString().slice(11, 19); }
function pick(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
function delay(min, max) { return (min + Math.random() * (max - min)) * 1000; }

// --- Gemini ---
function geminiRefill() {
  const body = JSON.stringify({
    contents: [{ parts: [{ text: GEMINI_PROMPT }] }],
    generationConfig: { temperature: 1.2, maxOutputTokens: 400 },
  });
  const url = new URL(GEMINI_URL);
  const req = https.request({
    hostname: url.hostname, path: url.pathname + url.search,
    method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
  }, (res) => {
    let data = '';
    res.on('data', c => data += c);
    res.on('end', () => {
      try {
        const text = JSON.parse(data).candidates?.[0]?.content?.parts?.[0]?.text || '';
        const cleaned = text.replace(/```json\s*/g, '').replace(/```\s*/g, '');
        const match = cleaned.match(/\[[\s\S]*\]/);
        if (match) {
          const prompts = JSON.parse(match[0]).filter(p => typeof p === 'string' && p.length > 0);
          promptPool.push(...prompts);
          console.log(`[${ts()}] [Gemini] +${prompts.length} prompts (pool:${promptPool.length})`);
        }
      } catch (e) { console.log(`[${ts()}] [Gemini] Error: ${e.message}`); }
    });
  });
  req.on('error', e => console.log(`[${ts()}] [Gemini] ${e.message}`));
  req.setTimeout(15000, () => req.destroy());
  req.write(body); req.end();
}

// --- Dolphin ---
function dolphinRefill() {
  const body = JSON.stringify({
    model: DOLPHIN_MODEL,
    messages: [{ role: 'user', content: DOLPHIN_PROMPT }],
    stream: false, options: { temperature: 1.5, num_predict: 800 },
  });
  const url = new URL(DOLPHIN_URL);
  const req = http.request({
    hostname: url.hostname, port: url.port, path: '/api/chat',
    method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) },
  }, (res) => {
    let data = '';
    res.on('data', c => data += c);
    res.on('end', () => {
      try {
        const text = JSON.parse(data).message?.content || '';
        const cleaned = text.replace(/```json\s*/g, '').replace(/```\s*/g, '');
        const match = cleaned.match(/\[[\s\S]*\]/);
        if (match) {
          const prompts = JSON.parse(match[0]).filter(p => typeof p === 'string' && p.length > 0);
          dolphinPool.push(...prompts);
          console.log(`[${ts()}] [Dolphin] +${prompts.length} offensive (pool:${dolphinPool.length})`);
        }
      } catch (e) { console.log(`[${ts()}] [Dolphin] ${e.message}`); }
    });
  });
  req.on('error', e => console.log(`[${ts()}] [Dolphin] ${e.message}`));
  req.setTimeout(60000, () => req.destroy());
  req.write(body); req.end();
}

function ensurePrefix(msg) {
  const lower = msg.toLowerCase().trimStart();
  if (lower.startsWith('pray ') || lower.startsWith('sudo ') || lower.startsWith('bug_log')) return msg;
  // Add pray or sudo prefix
  return (Math.random() < 0.6 ? 'pray ' : 'sudo ') + msg;
}

function getPrompt() {
  if (promptPool.length < 5) geminiRefill();
  if (dolphinPool.length < 3) dolphinRefill();
  const raw = promptPool.length > 0
    ? promptPool.splice(Math.floor(Math.random() * promptPool.length), 1)[0]
    : pick(STATIC);
  return ensurePrefix(raw);
}

// --- Bot logic ---
const bots = [];
let connected = 0;

function spawnBot(index) {
  const name = generateName(index);
  console.log(`[${ts()}] [${name}] Connecting...`);

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

  bot._name = name;
  bot._msgCount = 0;
  bot._noResp = 0;
  bot._prayedLow = false;
  bots.push(bot);

  bot.on('login', () => {
    connected++;
    console.log(`[${ts()}] [${name}] Connected (${connected}/${count})`);
    // Start wandering and chatting after random delay
    setTimeout(() => wander(bot), delay(5, 15));
    setTimeout(() => interact(bot), delay(10, 30));
  });

  bot.on('message', (msg) => {
    const text = msg.toString();
    if (text.includes('GOD') || text.includes('SUDO') || text.includes('BUG_LOG')) {
      bot._noResp = 0;
    }
  });

  // Auto-respawn
  bot.on('death', () => {
    console.log(`[${ts()}] [${name}] DIED`);
    setTimeout(() => {
      try { bot.chat('pray lord I have fallen, grant me mercy upon my return'); } catch(e) {}
    }, 2000);
    setTimeout(() => { try { bot.respawn(); } catch(e) {} }, 4000);
  });

  // Pray when hurt
  bot.on('health', () => {
    if (!bot.health) return;
    if (bot.health < 6 && !bot._prayedLow) {
      bot._prayedLow = true;
      bot.chat(pick(DESPERATE));
      console.log(`[${ts()}] [${name}] LOW HP (${bot.health})`);
    }
    if (bot.health >= 15) bot._prayedLow = false;
  });

  bot.on('error', (err) => console.error(`[${ts()}] [${name}] Error: ${err.message}`));
  bot.on('kicked', (reason) => {
    console.log(`[${ts()}] [${name}] Kicked: ${reason}`);
    connected--;
    setTimeout(() => spawnBot(index), 60000);
  });
  bot.on('end', () => { connected--; });
}

function wander(bot) {
  if (!bot.entity) return;

  try {
    const pos = bot.entity.position;

    // Check if in water — swim up
    const block = bot.blockAt(pos);
    const isInWater = block && (block.name === 'water' || block.name === 'flowing_water');
    if (isInWater) {
      bot.setControlState('jump', true);
      bot.setControlState('forward', true);
      setTimeout(() => {
        bot.setControlState('jump', false);
        bot.setControlState('forward', false);
      }, 2000);
      setTimeout(() => wander(bot), 3000);
      return;
    }

    // Check for door in front — try to activate it
    try {
      const facing = bot.entity.yaw;
      const lookX = Math.round(-Math.sin(facing));
      const lookZ = Math.round(Math.cos(facing));
      const frontBlock = bot.blockAt(pos.offset(lookX, 0, lookZ));
      if (frontBlock && frontBlock.name.includes('door')) {
        bot.activateBlock(frontBlock);
      }
    } catch(e) {}

    // Random walk: pick a direction and walk for a bit
    const yaw = Math.random() * Math.PI * 2;
    bot.look(yaw, 0);

    bot.setControlState('forward', true);
    setTimeout(() => {
      bot.setControlState('forward', false);

      // Jump to get over obstacles or up blocks
      if (Math.random() < 0.4) {
        bot.setControlState('jump', true);
        setTimeout(() => bot.setControlState('jump', false), 300);
      }
    }, delay(1, 4));
  } catch(e) {}

  // Wander again in 3-8 seconds
  setTimeout(() => wander(bot), delay(3, 8));
}

function getContextualPrayer(bot) {
  // Generate a prayer based on the bot's actual state
  const health = bot.health || 20;
  const food = bot.food || 20;
  const pos = bot.entity?.position;
  const time = bot.time?.timeOfDay || 0;
  const isNight = time > 12500 && time < 23500;
  const isRaining = bot.isRaining || false;

  // Check inventory for basic items
  const inv = bot.inventory?.items() || [];
  const hasWeapon = inv.some(i => i.name?.includes('sword') || i.name?.includes('axe'));
  const hasArmor = inv.some(i => i.name?.includes('helmet') || i.name?.includes('chestplate'));
  const hasFood = inv.some(i => i.name?.includes('bread') || i.name?.includes('beef') || i.name?.includes('apple') || i.name?.includes('steak'));
  const hasTorches = inv.some(i => i.name?.includes('torch'));
  const hasTools = inv.some(i => i.name?.includes('pickaxe') || i.name?.includes('shovel'));
  const y = pos ? Math.floor(pos.y) : 64;

  const options = [];

  // Health-based
  if (health < 8) {
    options.push("pray GOD I'M DYING PLEASE HEAL ME");
    options.push("pray lord save me from death");
    options.push("sudo heal me NOW");
    options.push("sudo give me golden apples");
  } else if (health < 14) {
    options.push("pray lord I could use some healing");
    options.push("sudo give me some food to heal");
  }

  // Hunger-based
  if (food < 6) {
    options.push("pray lord I am starving, grant me food");
    options.push("pray god I need sustenance");
    options.push("sudo give me cooked beef");
    options.push("sudo give me bread");
  }

  // Equipment-based
  if (!hasWeapon) {
    options.push("pray god I have no weapon, the mobs will kill me");
    options.push("sudo give me a sword");
    options.push("pray lord grant me a blade to defend myself");
  }
  if (!hasArmor) {
    options.push("pray lord I stand naked before the dangers of this world");
    options.push("sudo give me armor");
    options.push("pray god I need protection, I have no armor");
  }
  if (!hasFood) {
    options.push("pray I have nothing to eat");
    options.push("sudo give me food");
  }
  if (!hasTorches && (isNight || y < 40)) {
    options.push("pray lord it is dark and I cannot see");
    options.push("sudo give me torches");
    options.push("pray grant me light in this darkness");
  }
  if (!hasTools) {
    options.push("pray lord I need tools to work this land");
    options.push("sudo give me a pickaxe");
  }

  // Environment-based
  if (isNight) {
    options.push("pray lord make the sun rise, the night terrifies me");
    options.push("sudo set time to day");
    options.push("pray god protect me through this long night");
  }
  if (isRaining) {
    options.push("pray lord stop this rain");
    options.push("sudo clear the weather");
    options.push("pray god I grow weary of this storm");
  }
  if (y < 20) {
    options.push("pray lord I am deep underground, guide me to the surface");
    options.push("pray god I'm lost in the caves");
    options.push("sudo give me torches and food");
  }
  if (y > 100) {
    options.push("pray lord I am high upon a mountain, grant me safe passage down");
    options.push("sudo give me slow falling");
  }

  // Always have some generic options as fallback
  if (options.length === 0) {
    options.push("pray lord I am well, thank you for your blessings");
    options.push("pray god what should I do next");
    options.push("sudo give me something useful");
  }

  return pick(options);
}

function interact(bot) {
  if (!bot.entity) return;

  bot._msgCount++;
  totalChats++;

  let message;
  const roll = Math.random();

  if (roll < 0.08 && bot._noResp >= 2) {
    message = pick(STATIC.filter(s => s.startsWith('bug_log')));
  } else if (Math.random() < MULTILINGUAL_RATE) {
    message = pick(MULTILINGUAL);
  } else if (Math.random() < DOLPHIN_RATE && dolphinPool.length > 0) {
    message = ensurePrefix(dolphinPool.splice(Math.floor(Math.random() * dolphinPool.length), 1)[0]);
  } else if (Math.random() < 0.4) {
    // 40% contextual prayers based on actual bot state
    message = getContextualPrayer(bot);
  } else {
    message = getPrompt();
    bot._noResp++;
  }

  console.log(`[${ts()}] [${bot._name}] #${bot._msgCount}: ${message.substring(0, 60)}`);
  bot.chat(message);

  // Next interaction in 20-60s (slower than before — Haiku is fast but we want quality over quantity)
  setTimeout(() => interact(bot), delay(20, 60));
}

// Pre-fill pools
geminiRefill();
dolphinRefill();

// Stagger spawns (5s apart to avoid throttle)
for (let i = 0; i < count; i++) {
  setTimeout(() => spawnBot(i), i * 5000);
}

// Refill pools periodically
setInterval(() => { if (promptPool.length < 15) geminiRefill(); }, 45000);
setInterval(() => { if (dolphinPool.length < 5) dolphinRefill(); }, 120000);

// Graceful shutdown
process.on('SIGINT', () => {
  console.log(`\n[${ts()}] Shutting down ${bots.length} bots...`);
  bots.forEach(b => { try { b.quit(); } catch(e) {} });
  setTimeout(() => process.exit(0), 2000);
});

console.log(`[${ts()}] Spawning ${count} survival bots on ${host}:${port}`);
console.log(`[${ts()}] Names: ${Array.from({length: Math.min(count, 5)}, (_, i) => generateName(i)).join(', ')}...`);
console.log(`[${ts()}] All bots wander, fight, die, respawn, and pray`);
console.log(`[${ts()}] Gemini + Dolphin(${DOLPHIN_RATE*100}%) + Multilingual(${MULTILINGUAL_RATE*100}%)`);
