import { world, system, ItemStack } from "@minecraft/server";
import { variables } from "@minecraft/server-admin";

// ─── World Detection ─────────────────────────────────────────────

let PREFIX    = "";
let EGG_BLOCK = "";

try {
  const p = world.getDynamicProperty("egg_prefix");
  const b = world.getDynamicProperty("egg_block");
  if (p && b) { PREFIX = p; EGG_BLOCK = b; }
} catch (e) {}

if (!PREFIX) {
  let worldName = "";
  try { worldName = (variables.get("world_name") || "").toLowerCase(); } catch (e) {}

  if (worldName.includes("jamie"))     { PREFIX = "j"; EGG_BLOCK = "lime_glazed_terracotta"; }
  else if (worldName.includes("lyla")) { PREFIX = "l"; EGG_BLOCK = "purple_glazed_terracotta"; }
  else if (worldName.includes("mya"))  { PREFIX = "m"; EGG_BLOCK = "cyan_glazed_terracotta"; }
  else                                 { PREFIX = "?"; EGG_BLOCK = "yellow_glazed_terracotta"; }

  try {
    world.setDynamicProperty("egg_prefix", PREFIX);
    world.setDynamicProperty("egg_block",  EGG_BLOCK);
  } catch (e) {}
}

// ─── Egg Positions ───────────────────────────────────────────────
// 50 (dx, dz) offsets from the first player's spawn position.
// Three rings: close (5-20), medium (20-50), far (50-100).

const EGG_OFFSETS = [
  // Close ring — 12 eggs
  [ 8,  5], [-6,  9], [12, -3], [-10,  7], [ 5, -14], [-14, -5],
  [ 7, 11], [-8, -12], [15,  8], [-15,  3], [  4, 18], [ -3, -16],
  // Medium ring — 22 eggs
  [ 22, 15], [-25,  8], [18, -28], [-20, -22], [35,  5], [-38, 12],
  [ 30,-18], [-32, 25], [45, 10],  [-42,  -8], [15, 40], [-18, -35],
  [ 28, 35], [-30,-28], [48, -12], [-45,  20], [38,-32], [-22,  45],
  [ 12,-48], [-48,-15], [50,  22], [-38, -40],
  // Far ring — 16 eggs
  [ 65, 15], [-68, 22], [55, -48], [-58,  42], [ 72,  35], [-75, -18],
  [ 80,-55], [-82, 40], [58,  70], [-60, -65], [ 90,  12], [-85, -50],
  [ 45, 82], [-48, 78], [75, -70], [-70,  75],
];

// ─── Scene Names ─────────────────────────────────────────────────

const SCENE_NAMES = [
  "atop a barrel",
  "on a hay bale",
  "nestled in a flower patch",
  "perched on a log",
  "between fence posts",
  "on a crafting table",
  "on a mossy ledge",
  "hidden in a leaf cluster",
];

// ─── Egg Basket (Cross-Server Persistence) ────────────────────────
// A nether_star item with a custom nameTag carries egg counts between servers.
// nameTag format: "§6Egg Basket §8[j:N,l:N,m:N]"
// Tags (egg_j_1, etc.) track which specific eggs were found per-session.
// The basket is the authoritative count the lobby reads.

const BASKET_ITEM  = "minecraft:nether_star";
const BASKET_HEAD  = "§6Egg Basket ";
const BASKET_REGEX = /\[j:(\d+),l:(\d+),m:(\d+)\]/;

function getBasket(player) {
  try {
    const inv = player.getComponent("minecraft:inventory");
    if (!inv) return null;
    const container = inv.container;
    for (let i = 0; i < container.size; i++) {
      const item = container.getItem(i);
      if (item && item.typeId === BASKET_ITEM &&
          item.nameTag && item.nameTag.startsWith(BASKET_HEAD)) {
        return { item, slot: i, container };
      }
    }
  } catch (e) {}
  return null;
}

function getBasketCounts(player) {
  const result = { j: 0, l: 0, m: 0 };
  const b = getBasket(player);
  if (!b) return result;
  const m = b.item.nameTag.match(BASKET_REGEX);
  if (m) { result.j = +m[1]; result.l = +m[2]; result.m = +m[3]; }
  return result;
}

function updateBasket(player, j, l, m) {
  try {
    const nameTag = `${BASKET_HEAD}§8[j:${j},l:${l},m:${m}]`;
    const b = getBasket(player);
    if (b) {
      b.item.nameTag = nameTag;
      b.container.setItem(b.slot, b.item);
    } else {
      const item = new ItemStack(BASKET_ITEM, 1);
      item.nameTag = nameTag;
      const inv = player.getComponent("minecraft:inventory");
      if (inv) {
        const container = inv.container;
        for (let i = 0; i < container.size; i++) {
          if (!container.getItem(i)) {
            container.setItem(i, item);
            player.sendMessage("§6[Easter Eggs] §fYou received an §6Egg Basket§f — it tracks your egg count across all worlds! Keep it safe.");
            break;
          }
        }
      }
    }
  } catch (e) {}
}

// ─── Basket Initialisation ───────────────────────────────────────
// Give a fresh basket to players who don't have one yet.
// We do NOT reconstruct tags from missing blocks on re-join because a missing
// block might mean "never placed" (chunk not loaded) rather than "collected".
// The block existence check inside collectEgg() is the sole guard against
// re-collecting an already-found egg.

function giveBasketIfMissing(player) {
  if (!getBasket(player)) updateBasket(player, 0, 0, 0);
}

// ─── Cached Positions ────────────────────────────────────────────

let eggPositions = null;

function getEggPositions() {
  if (eggPositions !== null) return eggPositions;
  try {
    const json = world.getDynamicProperty("eggs_data");
    if (json && json.length > 2) {
      eggPositions = JSON.parse(json);
      return eggPositions;
    }
  } catch (e) {}
  return null;
}

// ─── Surface Scanner ─────────────────────────────────────────────

function findSurfaceY(dimension, x, spawnY, z) {
  const top    = Math.min(320, spawnY + 60);
  const bottom = Math.max(-60, spawnY - 30);
  for (let y = top; y >= bottom; y--) {
    try {
      const block = dimension.getBlock({ x, y, z });
      if (block && block.typeId !== "minecraft:air") {
        return y + 1; // first empty block above the topmost solid block
      }
    } catch (e) {}
  }
  return spawnY;
}

// ─── Scene Builder ───────────────────────────────────────────────
// Each egg is 2 blocks tall — bottom faces south (2), top faces north (3).
// This opposing rotation gives the glazed terracotta an oval/egg appearance.
// Stored Y is always the bottom block.

function buildScene(dim, sceneIndex, x, surfY, z) {
  const cmds = [];
  let eggY   = surfY;

  switch (sceneIndex % 8) {
    case 0: // Barrel nook — egg perched on a barrel
      cmds.push(`setblock ${x} ${surfY} ${z} barrel`);
      eggY = surfY + 1;
      break;

    case 1: // Hay bale — egg resting on a bale
      cmds.push(`setblock ${x} ${surfY} ${z} hay_block`);
      eggY = surfY + 1;
      break;

    case 2: // Flower patch — egg nestled among wildflowers
      cmds.push(`setblock ${x-1} ${surfY} ${z}   poppy`);
      cmds.push(`setblock ${x+1} ${surfY} ${z}   cornflower`);
      cmds.push(`setblock ${x}   ${surfY} ${z-1} dandelion`);
      eggY = surfY; // egg rises up through the flower level
      break;

    case 3: // Log perch — egg balanced atop a cut log
      cmds.push(`setblock ${x} ${surfY} ${z} oak_log`);
      eggY = surfY + 1;
      break;

    case 4: // Fence posts — egg elevated on a fence pedestal
      cmds.push(`setblock ${x-1} ${surfY} ${z} oak_fence`);
      cmds.push(`setblock ${x}   ${surfY} ${z} oak_fence`);
      cmds.push(`setblock ${x+1} ${surfY} ${z} oak_fence`);
      eggY = surfY + 1;
      break;

    case 5: // Crafting table — egg left on a workbench
      cmds.push(`setblock ${x} ${surfY} ${z} crafting_table`);
      eggY = surfY + 1;
      break;

    case 6: // Mossy ledge — egg raised on a stone platform
      cmds.push(`setblock ${x}   ${surfY} ${z} mossy_cobblestone`);
      cmds.push(`setblock ${x-1} ${surfY} ${z} mossy_cobblestone`);
      eggY = surfY + 1;
      break;

    case 7: // Leaf cluster — egg tucked in a low leaf canopy
      // Side leaves frame the egg without fully hiding it
      cmds.push(`setblock ${x+1} ${surfY+1} ${z}   oak_leaves`);
      cmds.push(`setblock ${x-1} ${surfY+1} ${z}   oak_leaves`);
      cmds.push(`setblock ${x}   ${surfY+1} ${z+1} oak_leaves`);
      cmds.push(`setblock ${x}   ${surfY+1} ${z-1} oak_leaves`);
      eggY = surfY + 1; // bottom block nestled in the leaf ring, top sticks above
      break;
  }

  cmds.push(`setblock ${x} ${eggY} ${z} ${EGG_BLOCK}`);

  for (const cmd of cmds) {
    try { dim.runCommand(cmd); } catch (e) {}
  }

  return eggY; // caller stores this as the bottom block Y
}

// ─── Egg Placement ───────────────────────────────────────────────

function placeAllEggs(playerPos) {
  const overworld = world.getDimension("overworld");

  if (world.getDynamicProperty("eggs_placed") === "true") {
    // Validate at least one stored egg block actually exists
    const stored = getEggPositions();
    if (stored && stored.length > 0) {
      // Check far-ring eggs (last 5) — they're hardest to reach and least likely
      // to have been collected yet. Avoids false "blocks missing" detection when
      // only the close-ring eggs (1-5) have been collected.
      let found = 0;
      const checkAt = [45, 46, 47, 48, 49].filter(i => i < stored.length);
      const fallback = checkAt.length === 0;
      const indices  = fallback ? [0, 1, 2, 3, 4].filter(i => i < stored.length) : checkAt;
      for (const i of indices) {
        try {
          const block = overworld.getBlock({ x: stored[i].x, y: stored[i].y, z: stored[i].z });
          if (block && block.typeId.includes("glazed_terracotta")) found++;
        } catch (e) {}
      }
      if (found > 0) return;
    }
    // Flagged as placed but blocks are absent — clear and re-place
    world.setDynamicProperty("eggs_placed", "false");
    world.setDynamicProperty("eggs_data",   "");
    world.setDynamicProperty("egg_center",  "");
    eggPositions = null;
  }

  // Determine centre: stored centre takes priority, then joining player, then world spawn
  let cx, cy, cz;
  try {
    const stored = JSON.parse(world.getDynamicProperty("egg_center") || "null");
    if (stored) { cx = stored.x; cy = stored.y; cz = stored.z; }
  } catch (e) {}

  if (cx === undefined) {
    if (playerPos) {
      cx = Math.floor(playerPos.x);
      cy = Math.floor(playerPos.y);
      cz = Math.floor(playerPos.z);
    } else {
      const spawn = world.getDefaultSpawnLocation();
      cx = Math.floor(spawn.x); cy = Math.floor(spawn.y); cz = Math.floor(spawn.z);
    }
    try { world.setDynamicProperty("egg_center", JSON.stringify({ x: cx, y: cy, z: cz })); } catch (e) {}
  }

  const positions = [];
  for (let i = 0; i < EGG_OFFSETS.length; i++) {
    const [dx, dz] = EGG_OFFSETS[i];
    const x     = cx + dx;
    const z     = cz + dz;
    const surfY = findSurfaceY(overworld, x, cy, z);
    const eggY  = buildScene(overworld, i, x, surfY, z);
    positions.push({ x, y: eggY, z, id: i + 1 });
  }

  try {
    world.setDynamicProperty("eggs_data",   JSON.stringify(positions));
    world.setDynamicProperty("eggs_placed", "true");
    world.sendMessage(`§6[Easter Eggs] §f${positions.length} eggs are now hidden around here — happy hunting!`);
  } catch (e) {
    world.sendMessage(`§c[Easter Eggs] §fFailed to save egg data: ${e.message}`);
  }
}

// ─── Egg Collection ──────────────────────────────────────────────

function collectEgg(player, egg, tagId) {
  try {
    // Confirm the block still exists before collecting (prevents ghost re-collection)
    const block = player.dimension.getBlock({ x: egg.x, y: egg.y, z: egg.z });
    if (!block || !block.typeId.includes("glazed_terracotta")) return;

    player.addTag(tagId);
    const count = player.getTags().filter(t => t.startsWith(`egg_${PREFIX}_`)).length;

    // Update basket with the new count for this world
    const counts = getBasketCounts(player);
    if      (PREFIX === "j") updateBasket(player, count, counts.l, counts.m);
    else if (PREFIX === "l") updateBasket(player, counts.j, count, counts.m);
    else if (PREFIX === "m") updateBasket(player, counts.j, counts.l, count);

    player.dimension.runCommand(`setblock ${egg.x} ${egg.y} ${egg.z} air`);
    player.dimension.runCommand(`particle minecraft:egg_destroy_emitter ${egg.x} ${egg.y} ${egg.z}`);
    player.runCommand(`playsound random.orb @s`);
    player.runCommand(`playsound random.levelup @s`);
    player.runCommand(`give @s emerald 1`);

    player.runCommand(`titleraw @s title {"rawtext":[{"text":"§6Easter Egg Found!"}]}`);
    player.runCommand(`titleraw @s subtitle {"rawtext":[{"text":"§e${count}/50 §7found in this world"}]}`);
    player.sendMessage(
      `§6[Eggs] §fEgg §e#${egg.id} §f(${SCENE_NAMES[(egg.id - 1) % 8]}) §7| §e${count}/50 §7in this world`
    );

    if (count === 50) {
      world.sendMessage(`§6§l✨ ${player.name} found all 50 eggs! ✨ §r§6Return to the lobby for your final score!`);
    }
  } catch (e) {}
}

// ─── Detection Loop ──────────────────────────────────────────────

system.runInterval(() => {
  const positions = getEggPositions();
  if (!positions || positions.length === 0) return;

  for (const player of world.getAllPlayers()) {
    const pos  = player.location;
    const tags = player.getTags();

    for (const egg of positions) {
      const tagId = `egg_${PREFIX}_${egg.id}`;
      if (tags.includes(tagId)) continue;

      const dx = Math.abs(pos.x - egg.x);
      const dy = Math.abs(pos.y - egg.y);
      const dz = Math.abs(pos.z - egg.z);

      if (dx <= 2.5 && dy <= 2.0 && dz <= 2.5) {
        collectEgg(player, egg, tagId);
      }
    }
  }
}, 10);

// ─── Chat Commands ───────────────────────────────────────────────

function handleEggCommand(player, msg) {
  if (msg === "!eggs") {
    const count = player.getTags().filter(t => t.startsWith(`egg_${PREFIX}_`)).length;
    player.sendMessage(`§6[Eggs] §fFound §e${count}§f/50 eggs in this world.`);
    if (count === 50) {
      player.sendMessage("§a§lYou found all 50! Return to the lobby to see the leaderboard.");
    } else {
      player.sendMessage("§7Hint: eggs are hidden in flower patches, on logs, barrels, hay bales, fences, crafting tables, mossy ledges, and leaf clusters.");
    }
    return true;
  }

  if (msg.startsWith("!seteggworld ")) {
    const arg = msg.split(" ")[1];
    if (!["j", "l", "m"].includes(arg)) {
      player.sendMessage("§c[Eggs] Usage: §e!seteggworld j§c (Jamie), §el§c (Lyla), §em§c (Mya)");
      return true;
    }
    const blockMap = { j: "lime_glazed_terracotta", l: "purple_glazed_terracotta", m: "cyan_glazed_terracotta" };
    PREFIX    = arg;
    EGG_BLOCK = blockMap[arg];
    try {
      world.setDynamicProperty("egg_prefix",  PREFIX);
      world.setDynamicProperty("egg_block",   EGG_BLOCK);
      world.setDynamicProperty("eggs_placed", "false");
      world.setDynamicProperty("eggs_data",   "");
    } catch (e) {}
    eggPositions = null;
    player.sendMessage(`§6[Eggs] §fWorld prefix set to §e${arg}§f — re-placing eggs...`);
    system.runTimeout(() => placeAllEggs(player.location), 20);
    return true;
  }

  if (msg === "!reseteggsworld") {
    try {
      world.setDynamicProperty("eggs_placed", "false");
      world.setDynamicProperty("eggs_data",   "");
      world.setDynamicProperty("egg_center",  "");
    } catch (e) {}
    eggPositions = null;
    eggsPlacedThisSession = false;
    player.sendMessage("§6[Eggs] §fEgg data cleared — eggs will re-place around your position...");
    const pos = player.location;
    system.runTimeout(() => placeAllEggs(pos), 20);
    return true;
  }

  return false;
}

try {
  world.beforeEvents.chatSend.subscribe((event) => {
    const msg = event.message.trim().toLowerCase();
    if (msg === "!eggs" || msg.startsWith("!seteggworld ") || msg === "!reseteggsworld") {
      event.cancel = true;
      const player = event.sender;
      system.run(() => handleEggCommand(player, msg));
    }
  });
} catch (e) {}

system.afterEvents.scriptEventReceive.subscribe((event) => {
  if (event.id !== "eggs:cmd") return;
  const msg = (event.message || "").trim().toLowerCase();

  // Allow reseteggsworld from console (sourceEntity is null when run via MCP/RCON)
  if (msg === "reseteggsworld") {
    try {
      world.setDynamicProperty("eggs_placed", "false");
      world.setDynamicProperty("eggs_data",   "");
      world.setDynamicProperty("egg_center",  "");
    } catch (e) {}
    eggPositions = null;
    eggsPlacedThisSession = false;
    world.sendMessage("§6[Eggs] §fEgg data cleared — eggs will re-place on next player spawn.");
    return;
  }

  const player = event.sourceEntity;
  if (!player) return;
  handleEggCommand(player, `!${msg}`);
});

// ─── Startup ─────────────────────────────────────────────────────
// Egg placement fires on first player spawn so chunks are guaranteed loaded.
// Tag rebuild fires for every player that (re)joins — recovers lost tags from
// cross-server transfers by checking which egg blocks are actually missing.

let eggsPlacedThisSession = false;

world.afterEvents.playerSpawn.subscribe((event) => {
  const player    = event.player;
  const playerPos = player.location;

  // Place eggs on first spawn of the session (40 tick delay to settle chunks)
  if (!eggsPlacedThisSession) {
    eggsPlacedThisSession = true;
    system.runTimeout(() => placeAllEggs(playerPos), 40);
  }

  // Give basket if player doesn't have one yet
  system.runTimeout(() => {
    try { giveBasketIfMissing(player); } catch (e) {}
  }, 40);
});

system.run(() => {
  world.sendMessage("§6[Easter Eggs] §fHunt active! Type §e!eggs §fto check your progress.");
});
