import { world, system } from "@minecraft/server";

// ─── Configuration ────────────────────────────────────────────────────────────

const SCAN_INTERVAL_TICKS = 6000;      // How often to scan for villages (5 min)
const CLUSTER_RADIUS = 48;             // Max distance (blocks) between villagers in same village
const MIN_VILLAGERS_TO_TRIGGER = 3;    // Minimum villagers before any expansion happens
const BUILD_QUEUE_RATE = 20;           // Max blocks placed per tick during construction
const MAX_PLOT_SEARCH_RADIUS = 80;     // How far to search for a valid building plot
const PLOT_CLEAR_SIZE = 7;             // Square size to check for obstructions
const DYNAMIC_PROP_KEY = "village_state"; // Key for world dynamic property persistence
const DYNAMIC_PROP_MAX = 32000;        // Max chars for a single dynamic property value

// ─── Building Templates ───────────────────────────────────────────────────────
// Each template is an array of [dx, dy, dz, blockId, blockStates?]
// dx/dz are relative to plot origin (northwest corner at ground level), dy is height offset

const TEMPLATES = {

  LAMP_POST: {
    name: "lamp post",
    footprint: 1,
    blocks: [
      [0, 0, 0, "oak_fence"],
      [0, 1, 0, "oak_fence"],
      [0, 2, 0, "lantern", '["hanging":false]'],
    ],
  },

  WELL: {
    name: "well",
    footprint: 3,
    blocks: [
      // Base ring of cobblestone
      [0, 0, 0, "cobblestone"], [1, 0, 0, "cobblestone"], [2, 0, 0, "cobblestone"],
      [0, 0, 1, "cobblestone"],                            [2, 0, 1, "cobblestone"],
      [0, 0, 2, "cobblestone"], [1, 0, 2, "cobblestone"], [2, 0, 2, "cobblestone"],
      // Water in the center
      [1, 0, 1, "water"],
      // Raised walls (level 1)
      [0, 1, 0, "cobblestone_wall"], [1, 1, 0, "cobblestone_wall"], [2, 1, 0, "cobblestone_wall"],
      [0, 1, 1, "cobblestone_wall"],                                 [2, 1, 1, "cobblestone_wall"],
      [0, 1, 2, "cobblestone_wall"], [1, 1, 2, "cobblestone_wall"], [2, 1, 2, "cobblestone_wall"],
      // Roof supports
      [0, 2, 0, "oak_fence"], [2, 2, 0, "oak_fence"],
      [0, 2, 2, "oak_fence"], [2, 2, 2, "oak_fence"],
      // Roof ridge
      [0, 3, 0, "oak_slab"], [1, 3, 0, "oak_slab"], [2, 3, 0, "oak_slab"],
      [0, 3, 1, "oak_slab"], [1, 3, 1, "oak_slab"], [2, 3, 1, "oak_slab"],
    ],
  },

  SMALL_HOUSE: {
    name: "small house",
    footprint: 7,
    blocks: [
      // Floor
      [0,0,0,"oak_planks"],[1,0,0,"oak_planks"],[2,0,0,"oak_planks"],[3,0,0,"oak_planks"],[4,0,0,"oak_planks"],
      [0,0,1,"oak_planks"],[1,0,1,"oak_planks"],[2,0,1,"oak_planks"],[3,0,1,"oak_planks"],[4,0,1,"oak_planks"],
      [0,0,2,"oak_planks"],[1,0,2,"oak_planks"],[2,0,2,"oak_planks"],[3,0,2,"oak_planks"],[4,0,2,"oak_planks"],
      [0,0,3,"oak_planks"],[1,0,3,"oak_planks"],[2,0,3,"oak_planks"],[3,0,3,"oak_planks"],[4,0,3,"oak_planks"],
      [0,0,4,"oak_planks"],[1,0,4,"oak_planks"],[2,0,4,"oak_planks"],[3,0,4,"oak_planks"],[4,0,4,"oak_planks"],
      // Walls - south face (z=0)
      [0,1,0,"oak_log"],[1,1,0,"oak_planks"],[2,1,0,"oak_planks"],[3,1,0,"oak_planks"],[4,1,0,"oak_log"],
      [0,2,0,"oak_log"],[1,2,0,"glass_pane"],[2,2,0,"oak_planks"],[3,2,0,"glass_pane"],[4,2,0,"oak_log"],
      [0,3,0,"oak_log"],[1,3,0,"oak_planks"],[2,3,0,"oak_planks"],[3,3,0,"oak_planks"],[4,3,0,"oak_log"],
      // Walls - north face (z=4)
      [0,1,4,"oak_log"],[1,1,4,"oak_planks"],[2,1,4,"oak_planks"],[3,1,4,"oak_planks"],[4,1,4,"oak_log"],
      [0,2,4,"oak_log"],[1,2,4,"glass_pane"],[2,2,4,"oak_planks"],[3,2,4,"glass_pane"],[4,2,4,"oak_log"],
      [0,3,4,"oak_log"],[1,3,4,"oak_planks"],[2,3,4,"oak_planks"],[3,3,4,"oak_planks"],[4,3,4,"oak_log"],
      // Walls - west face (x=0)
      [0,1,1,"oak_planks"],[0,1,2,"oak_planks"],[0,1,3,"oak_planks"],
      [0,2,1,"oak_planks"],[0,2,2,"glass_pane"],[0,2,3,"oak_planks"],
      [0,3,1,"oak_planks"],[0,3,2,"oak_planks"],[0,3,3,"oak_planks"],
      // Walls - east face (x=4) with door gap at y=1,2 z=2
      [4,1,1,"oak_planks"],[4,1,3,"oak_planks"],
      [4,2,1,"oak_planks"],[4,2,3,"oak_planks"],
      [4,3,1,"oak_planks"],[4,3,2,"oak_planks"],[4,3,3,"oak_planks"],
      // Door (east face, z=2) - oak door bottom then top
      [4,1,2,"oak_door",'["door_hinge_bit":false,"open_bit":false,"upper_block_bit":false,"direction":0]'],
      [4,2,2,"oak_door",'["door_hinge_bit":false,"open_bit":false,"upper_block_bit":true,"direction":0]'],
      // Roof (spruce slabs)
      [0,4,0,"spruce_slab"],[1,4,0,"spruce_slab"],[2,4,0,"spruce_slab"],[3,4,0,"spruce_slab"],[4,4,0,"spruce_slab"],
      [0,4,1,"spruce_slab"],[1,4,1,"spruce_slab"],[2,4,1,"spruce_slab"],[3,4,1,"spruce_slab"],[4,4,1,"spruce_slab"],
      [0,4,2,"spruce_slab"],[1,4,2,"spruce_slab"],[2,4,2,"spruce_slab"],[3,4,2,"spruce_slab"],[4,4,2,"spruce_slab"],
      [0,4,3,"spruce_slab"],[1,4,3,"spruce_slab"],[2,4,3,"spruce_slab"],[3,4,3,"spruce_slab"],[4,4,3,"spruce_slab"],
      [0,4,4,"spruce_slab"],[1,4,4,"spruce_slab"],[2,4,4,"spruce_slab"],[3,4,4,"spruce_slab"],[4,4,4,"spruce_slab"],
      // Interior torch
      [2,2,2,"torch"],
    ],
  },

  FARM_PLOT: {
    name: "farm",
    footprint: 9,
    blocks: [
      // Farmland rows (8x1 strips with water channel in middle)
      // Water channel at x=4
      [4,0,0,"water"],[4,0,1,"water"],[4,0,2,"water"],[4,0,3,"water"],
      [4,0,4,"water"],[4,0,5,"water"],[4,0,6,"water"],[4,0,7,"water"],
      // Farmland west of channel
      [0,0,0,"farmland"],[1,0,0,"farmland"],[2,0,0,"farmland"],[3,0,0,"farmland"],
      [0,0,1,"farmland"],[1,0,1,"farmland"],[2,0,1,"farmland"],[3,0,1,"farmland"],
      [0,0,2,"farmland"],[1,0,2,"farmland"],[2,0,2,"farmland"],[3,0,2,"farmland"],
      [0,0,3,"farmland"],[1,0,3,"farmland"],[2,0,3,"farmland"],[3,0,3,"farmland"],
      [0,0,4,"farmland"],[1,0,4,"farmland"],[2,0,4,"farmland"],[3,0,4,"farmland"],
      [0,0,5,"farmland"],[1,0,5,"farmland"],[2,0,5,"farmland"],[3,0,5,"farmland"],
      [0,0,6,"farmland"],[1,0,6,"farmland"],[2,0,6,"farmland"],[3,0,6,"farmland"],
      [0,0,7,"farmland"],[1,0,7,"farmland"],[2,0,7,"farmland"],[3,0,7,"farmland"],
      // Farmland east of channel
      [5,0,0,"farmland"],[6,0,0,"farmland"],[7,0,0,"farmland"],[8,0,0,"farmland"],
      [5,0,1,"farmland"],[6,0,1,"farmland"],[7,0,1,"farmland"],[8,0,1,"farmland"],
      [5,0,2,"farmland"],[6,0,2,"farmland"],[7,0,2,"farmland"],[8,0,2,"farmland"],
      [5,0,3,"farmland"],[6,0,3,"farmland"],[7,0,3,"farmland"],[8,0,3,"farmland"],
      [5,0,4,"farmland"],[6,0,4,"farmland"],[7,0,4,"farmland"],[8,0,4,"farmland"],
      [5,0,5,"farmland"],[6,0,5,"farmland"],[7,0,5,"farmland"],[8,0,5,"farmland"],
      [5,0,6,"farmland"],[6,0,6,"farmland"],[7,0,6,"farmland"],[8,0,6,"farmland"],
      [5,0,7,"farmland"],[6,0,7,"farmland"],[7,0,7,"farmland"],[8,0,7,"farmland"],
      // Crops on farmland
      [0,1,0,"wheat"],[2,1,0,"wheat"],[6,1,0,"carrots"],[8,1,0,"potatoes"],
      [0,1,2,"wheat"],[2,1,2,"wheat"],[6,1,2,"carrots"],[8,1,2,"potatoes"],
      [0,1,4,"wheat"],[2,1,4,"wheat"],[6,1,4,"carrots"],[8,1,4,"potatoes"],
      [0,1,6,"wheat"],[2,1,6,"wheat"],[6,1,6,"carrots"],[8,1,6,"potatoes"],
      // Fence border
      [0,-1,0,"oak_fence"],[1,-1,0,"oak_fence"],[2,-1,0,"oak_fence"],[3,-1,0,"oak_fence"],
      [4,-1,0,"oak_fence"],[5,-1,0,"oak_fence"],[6,-1,0,"oak_fence"],[7,-1,0,"oak_fence"],[8,-1,0,"oak_fence"],
      [0,-1,7,"oak_fence"],[1,-1,7,"oak_fence"],[2,-1,7,"oak_fence"],[3,-1,7,"oak_fence"],
      [4,-1,7,"oak_fence"],[5,-1,7,"oak_fence"],[6,-1,7,"oak_fence"],[7,-1,7,"oak_fence"],[8,-1,7,"oak_fence"],
      [0,-1,1,"oak_fence"],[0,-1,2,"oak_fence"],[0,-1,3,"oak_fence"],[0,-1,4,"oak_fence"],[0,-1,5,"oak_fence"],[0,-1,6,"oak_fence"],
      [8,-1,1,"oak_fence"],[8,-1,2,"oak_fence"],[8,-1,3,"oak_fence"],[8,-1,4,"oak_fence"],[8,-1,5,"oak_fence"],[8,-1,6,"oak_fence"],
      // Gate on south side
      [4,-1,0,"oak_fence_gate",'["direction":0,"open_bit":false,"in_wall_bit":false]'],
    ],
  },

  BLACKSMITH: {
    name: "blacksmith",
    footprint: 9,
    blocks: [
      // Foundation
      [0,0,0,"cobblestone"],[1,0,0,"cobblestone"],[2,0,0,"cobblestone"],[3,0,0,"cobblestone"],[4,0,0,"cobblestone"],[5,0,0,"cobblestone"],[6,0,0,"cobblestone"],
      [0,0,1,"cobblestone"],[1,0,1,"stone_bricks"],[2,0,1,"stone_bricks"],[3,0,1,"stone_bricks"],[4,0,1,"stone_bricks"],[5,0,1,"stone_bricks"],[6,0,1,"cobblestone"],
      [0,0,2,"cobblestone"],[1,0,2,"stone_bricks"],[2,0,2,"stone_bricks"],[3,0,2,"stone_bricks"],[4,0,2,"stone_bricks"],[5,0,2,"stone_bricks"],[6,0,2,"cobblestone"],
      [0,0,3,"cobblestone"],[1,0,3,"stone_bricks"],[2,0,3,"stone_bricks"],[3,0,3,"stone_bricks"],[4,0,3,"stone_bricks"],[5,0,3,"stone_bricks"],[6,0,3,"cobblestone"],
      [0,0,4,"cobblestone"],[1,0,4,"cobblestone"],[2,0,4,"cobblestone"],[3,0,4,"cobblestone"],[4,0,4,"cobblestone"],[5,0,4,"cobblestone"],[6,0,4,"cobblestone"],
      // Walls level 1
      [0,1,0,"stone_brick_wall"],[6,1,0,"stone_brick_wall"],
      [0,1,1,"stone_brick_wall"],[6,1,1,"stone_brick_wall"],
      [0,1,2,"stone_brick_wall"],[6,1,2,"stone_brick_wall"],
      [0,1,3,"stone_brick_wall"],[6,1,3,"stone_brick_wall"],
      [0,1,4,"stone_brick_wall"],[1,1,4,"stone_brick_wall"],[2,1,4,"stone_brick_wall"],[3,1,4,"stone_brick_wall"],[4,1,4,"stone_brick_wall"],[5,1,4,"stone_brick_wall"],[6,1,4,"stone_brick_wall"],
      // Back wall with window
      [0,1,0,"stone_bricks"],[1,1,0,"stone_bricks"],[2,1,0,"stone_bricks"],[3,1,0,"stone_bricks"],[4,1,0,"stone_bricks"],[5,1,0,"stone_bricks"],[6,1,0,"stone_bricks"],
      [0,2,0,"stone_bricks"],[1,2,0,"stone_bricks"],[2,2,0,"glass_pane"],[3,2,0,"glass_pane"],[4,2,0,"glass_pane"],[5,2,0,"stone_bricks"],[6,2,0,"stone_bricks"],
      [0,3,0,"stone_bricks"],[1,3,0,"stone_bricks"],[2,3,0,"stone_bricks"],[3,3,0,"stone_bricks"],[4,3,0,"stone_bricks"],[5,3,0,"stone_bricks"],[6,3,0,"stone_bricks"],
      // Side walls
      [0,2,1,"stone_bricks"],[0,2,2,"stone_bricks"],[0,2,3,"stone_bricks"],
      [0,3,1,"stone_bricks"],[0,3,2,"stone_bricks"],[0,3,3,"stone_bricks"],
      [6,2,1,"stone_bricks"],[6,2,2,"stone_bricks"],[6,2,3,"stone_bricks"],
      [6,3,1,"stone_bricks"],[6,3,2,"stone_bricks"],[6,3,3,"stone_bricks"],
      // Front open face (partial) - pillars only
      [0,2,4,"stone_bricks"],[0,3,4,"stone_bricks"],
      [6,2,4,"stone_bricks"],[6,3,4,"stone_bricks"],
      // Roof slabs
      [0,4,0,"stone_brick_slab"],[1,4,0,"stone_brick_slab"],[2,4,0,"stone_brick_slab"],[3,4,0,"stone_brick_slab"],[4,4,0,"stone_brick_slab"],[5,4,0,"stone_brick_slab"],[6,4,0,"stone_brick_slab"],
      [0,4,1,"stone_brick_slab"],[1,4,1,"stone_brick_slab"],[2,4,1,"stone_brick_slab"],[3,4,1,"stone_brick_slab"],[4,4,1,"stone_brick_slab"],[5,4,1,"stone_brick_slab"],[6,4,1,"stone_brick_slab"],
      [0,4,2,"stone_brick_slab"],[1,4,2,"stone_brick_slab"],[2,4,2,"stone_brick_slab"],[3,4,2,"stone_brick_slab"],[4,4,2,"stone_brick_slab"],[5,4,2,"stone_brick_slab"],[6,4,2,"stone_brick_slab"],
      [0,4,3,"stone_brick_slab"],[1,4,3,"stone_brick_slab"],[2,4,3,"stone_brick_slab"],[3,4,3,"stone_brick_slab"],[4,4,3,"stone_brick_slab"],[5,4,3,"stone_brick_slab"],[6,4,3,"stone_brick_slab"],
      // Interior furnace and crafting table
      [1,1,1,"furnace",'["facing_direction":3]'],
      [2,1,1,"furnace",'["facing_direction":3]'],
      [4,1,1,"crafting_table"],
      [5,1,1,"chest",'["facing_direction":3]'],
      // Lava source for forge effect (sunken into floor, covered by grate effect)
      [2,0,2,"lava"],
      // Lava light
      [3,1,1,"torch"],
    ],
  },
};

// ─── Tier → building sequence ─────────────────────────────────────────────────
// Each tier entry: what to build when village reaches that population
const TIER_BUILDS = [
  { minPop: 3,  tier: 1, template: "LAMP_POST",   label: "a lamp post" },
  { minPop: 3,  tier: 1, template: "WELL",         label: "a well" },
  { minPop: 6,  tier: 2, template: "SMALL_HOUSE",  label: "a house" },
  { minPop: 8,  tier: 2, template: "SMALL_HOUSE",  label: "another house" },
  { minPop: 10, tier: 3, template: "FARM_PLOT",    label: "a farm" },
  { minPop: 12, tier: 3, template: "SMALL_HOUSE",  label: "a third house" },
  { minPop: 15, tier: 4, template: "BLACKSMITH",   label: "a blacksmith" },
];

// ─── State Management ─────────────────────────────────────────────────────────

/** @type {{ [villageId: string]: { center: {x:number,y:number,z:number}, buildIndex: number, builtPlots: [number,number][], lastScanTick: number } }} */
let villageRegistry = {};

function loadState() {
  try {
    const raw = world.getDynamicProperty(DYNAMIC_PROP_KEY);
    if (raw && typeof raw === "string") {
      villageRegistry = JSON.parse(raw);
    }
  } catch (e) {
    world.sendMessage("§c[Village Evolution] Failed to load state: " + e.message);
    villageRegistry = {};
  }
}

function saveState() {
  try {
    const serialized = JSON.stringify(villageRegistry);
    if (serialized.length > DYNAMIC_PROP_MAX) {
      // Trim oldest built plots to stay under limit
      for (const id of Object.keys(villageRegistry)) {
        if (villageRegistry[id].builtPlots.length > 5) {
          villageRegistry[id].builtPlots = villageRegistry[id].builtPlots.slice(-5);
        }
      }
    }
    world.setDynamicProperty(DYNAMIC_PROP_KEY, JSON.stringify(villageRegistry));
  } catch (e) {
    world.sendMessage("§c[Village Evolution] Failed to save state: " + e.message);
  }
}

// ─── Village Detection ────────────────────────────────────────────────────────

function dist2D(a, b) {
  const dx = a.x - b.x;
  const dz = a.z - b.z;
  return Math.sqrt(dx * dx + dz * dz);
}

/**
 * Cluster villager locations into groups using simple single-linkage clustering.
 * Returns array of clusters, each cluster is array of locations.
 */
function clusterVillagers(locations) {
  const clusters = [];
  const assigned = new Array(locations.length).fill(false);

  for (let i = 0; i < locations.length; i++) {
    if (assigned[i]) continue;
    const cluster = [locations[i]];
    assigned[i] = true;

    // Grow cluster by proximity
    let changed = true;
    while (changed) {
      changed = false;
      for (let j = 0; j < locations.length; j++) {
        if (assigned[j]) continue;
        for (const member of cluster) {
          if (dist2D(locations[j], member) <= CLUSTER_RADIUS) {
            cluster.push(locations[j]);
            assigned[j] = true;
            changed = true;
            break;
          }
        }
      }
    }
    clusters.push(cluster);
  }
  return clusters;
}

/**
 * Compute centroid of a cluster and snap to a stable ID grid (32-block grid).
 */
function clusterToVillage(cluster) {
  const cx = cluster.reduce((s, v) => s + v.x, 0) / cluster.length;
  const cy = cluster.reduce((s, v) => s + v.y, 0) / cluster.length;
  const cz = cluster.reduce((s, v) => s + v.z, 0) / cluster.length;

  // Snap to 32-block grid for stable ID
  const gridX = Math.round(cx / 32) * 32;
  const gridZ = Math.round(cz / 32) * 32;
  const id = `${gridX}_${gridZ}`;

  return {
    id,
    center: { x: Math.floor(cx), y: Math.floor(cy), z: Math.floor(cz) },
    population: cluster.length,
  };
}

// ─── Plot Finding ─────────────────────────────────────────────────────────────

const SAFE_GROUND_BLOCKS = new Set([
  "minecraft:grass_block", "minecraft:dirt", "minecraft:coarse_dirt",
  "minecraft:podzol", "minecraft:grass_path", "minecraft:farmland",
  "minecraft:sand", "minecraft:gravel", "minecraft:stone",
  "minecraft:cobblestone", "minecraft:oak_planks", "minecraft:spruce_planks",
]);

const UNSAFE_BLOCKS = new Set([
  "minecraft:water", "minecraft:flowing_water", "minecraft:lava",
  "minecraft:flowing_lava", "minecraft:bedrock", "minecraft:obsidian",
]);

/**
 * Check whether a ground block exists at (x, y, z) and the space above is clear.
 */
function isPlotClear(dimension, ox, oy, oz, size) {
  for (let dx = 0; dx < size; dx++) {
    for (let dz = 0; dz < size; dz++) {
      const gx = ox + dx;
      const gz = oz + dz;

      // Ground block check
      const ground = dimension.getBlock({ x: gx, y: oy - 1, z: gz });
      if (!ground) return false;
      if (UNSAFE_BLOCKS.has(ground.typeId)) return false;

      // Space above (2 blocks) must be air or leaves
      for (let dy = 0; dy <= 3; dy++) {
        const above = dimension.getBlock({ x: gx, y: oy + dy, z: gz });
        if (!above) return false;
        const tid = above.typeId;
        if (tid !== "minecraft:air" && !tid.includes("leaves") && !tid.includes("grass") && !tid.includes("flower") && !tid.includes("tallgrass")) {
          return false;
        }
      }
    }
  }
  return true;
}

/**
 * Find a valid plot near the village center using an outward spiral search.
 * Returns {x, y, z} origin of plot or null if none found.
 */
function findPlot(dimension, center, footprint, builtPlots) {
  const halfSize = Math.ceil(footprint / 2);
  const ground = center.y;

  // Spiral outward from center
  let x = 0, z = 0;
  let step = 1, turn = 0, stepCount = 0, totalSteps = 0;

  while (totalSteps < 400) {
    const wx = center.x + x * 8 - halfSize;
    const wz = center.z + z * 8 - halfSize;

    // Skip if too close to an existing built plot
    const tooClose = builtPlots.some(([px, pz]) => {
      return Math.abs(px - wx) < footprint + 4 && Math.abs(pz - wz) < footprint + 4;
    });

    if (!tooClose) {
      // Search vertically for the right ground level
      for (let dy = -5; dy <= 5; dy++) {
        const testY = ground + dy;
        if (isPlotClear(dimension, wx, testY, wz, footprint)) {
          const dist = Math.sqrt(x * x + z * z) * 8;
          if (dist <= MAX_PLOT_SEARCH_RADIUS) {
            return { x: wx, y: testY, z: wz };
          }
        }
      }
    }

    // Advance spiral
    if (x === 0 && z === 0) { x = 1; totalSteps++; continue; }

    stepCount++;
    if (turn === 0) z--;
    else if (turn === 1) x--;
    else if (turn === 2) z++;
    else if (turn === 3) x++;

    if (stepCount >= step) {
      stepCount = 0;
      turn = (turn + 1) % 4;
      if (turn === 0 || turn === 2) step++;
    }
    totalSteps++;
  }

  return null;
}

// ─── Block Placement Queue ────────────────────────────────────────────────────

/** @type {Array<{dimension: any, cmd: string}>} */
let buildQueue = [];
let buildQueueInterval = null;

function enqueueBuild(dimension, template, ox, oy, oz) {
  for (const [dx, dy, dz, blockId, states] of template.blocks) {
    const x = ox + dx;
    const y = oy + dy;
    const z = oz + dz;
    const stateStr = states ? ` ${states}` : "";
    buildQueue.push({ dimension, cmd: `setblock ${x} ${y} ${z} ${blockId}${stateStr} replace` });
  }
}

function startBuildQueue() {
  if (buildQueueInterval !== null) return;
  buildQueueInterval = system.runInterval(() => {
    if (buildQueue.length === 0) {
      system.clearRun(buildQueueInterval);
      buildQueueInterval = null;
      return;
    }
    const batch = buildQueue.splice(0, BUILD_QUEUE_RATE);
    for (const { dimension, cmd } of batch) {
      try {
        dimension.runCommand(cmd);
      } catch (_) {
        // Non-fatal — chunk may not be loaded
      }
    }
  }, 1);
}

// ─── Village Expansion Logic ──────────────────────────────────────────────────

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

  let allVillagers;
  try {
    allVillagers = overworld.getEntities({ type: "minecraft:villager" });
  } catch (e) {
    return; // World not ready
  }

  if (allVillagers.length === 0) return;

  const locations = allVillagers.map(v => v.location);
  const clusters = clusterVillagers(locations);

  for (const cluster of clusters) {
    if (cluster.length < MIN_VILLAGERS_TO_TRIGGER) continue;

    const village = clusterToVillage(cluster);
    const id = village.id;

    // Initialize registry entry if new village
    if (!villageRegistry[id]) {
      villageRegistry[id] = {
        center: village.center,
        buildIndex: 0,
        builtPlots: [],
        lastScanTick: 0,
      };
    }

    const state = villageRegistry[id];

    // Update center (villages drift slightly as villagers move)
    state.center = village.center;

    const buildIndex = state.buildIndex;
    if (buildIndex >= TIER_BUILDS.length) continue; // Village fully developed

    const nextBuild = TIER_BUILDS[buildIndex];
    if (village.population < nextBuild.minPop) continue; // Not enough villagers yet

    // Find a valid plot
    const template = TEMPLATES[nextBuild.template];
    const plot = findPlot(overworld, village.center, template.footprint, state.builtPlots);

    if (!plot) {
      // No valid plot found — skip this cycle, try again next scan
      continue;
    }

    // Clear the plot (remove grass/flowers above)
    for (let dx = 0; dx < template.footprint; dx++) {
      for (let dz = 0; dz < template.footprint; dz++) {
        for (let dy = 0; dy <= 3; dy++) {
          buildQueue.push({
            dimension: overworld,
            cmd: `setblock ${plot.x + dx} ${plot.y + dy} ${plot.z + dz} air replace`,
          });
        }
      }
    }

    // Enqueue the building
    enqueueBuild(overworld, template, plot.x, plot.y, plot.z);
    startBuildQueue();

    // Update state
    state.builtPlots.push([plot.x, plot.z]);
    state.buildIndex++;
    state.lastScanTick = system.currentTick;
    saveState();

    // Announce
    world.sendMessage(
      `§a[Village] §7A village near §e(${village.center.x}, ${village.center.z})§7 has grown — ${nextBuild.label} has been built!`
    );
  }
}

// ─── Startup ──────────────────────────────────────────────────────────────────

// Register dynamic property schema (max string length)
world.afterEvents.worldInitialize.subscribe((event) => {
  try {
    event.propertyRegistry.defineWorldProperty({
      id: DYNAMIC_PROP_KEY,
      type: "string",
      maxLength: DYNAMIC_PROP_MAX,
    });
  } catch (_) {
    // Already registered (e.g. pack was loaded before)
  }
});

system.run(() => {
  loadState();
  world.sendMessage("§a[Village Evolution] §7Village growth system active.");

  // Main scan loop
  system.runInterval(() => {
    runVillageScan();
  }, SCAN_INTERVAL_TICKS);
});
