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

const CROP = "silverlabs:hemp_crop";
const SUN_LAMP = "silverlabs:sun_lamp";
const SEEDS = "silverlabs:hemp_seeds";
const BUD = "silverlabs:hemp_bud";
const TINCTURE = "silverlabs:hemp_tincture";
const BROWNIE = "silverlabs:hemp_brownie";

const AGE = "silverlabs:hemp_age";
const TOP = "silverlabs:hemp_top";

const GROWTH_INTERVAL_TICKS = 100;   // 5s between growth ticks
const RABBIT_INTERVAL_TICKS = 100;   // 5s between rabbit checks
const SCAN_RADIUS = 5;                // ±5 horizontal — dense scan, every block looked at
const SCAN_VERT = 2;                  // ±2 vertical
const SUN_LAMP_RANGE = 4;             // blocks searched for a lamp around indoor crop
const GROWTH_CHANCE_OUTDOOR = 0.017; // per 5s tick — ~20 min for 0→4 (prime)
const GROWTH_CHANCE_INDOOR = 0.011;  // per 5s tick — ~30 min for 0→4 (sun lamp required)
const OVERRIPE_CHANCE_MULT = 0.33;   // age 4→5 transition is 3x slower so prime lingers

function rand(n) { return Math.floor(Math.random() * n); }
function chance(p) { return Math.random() < p; }
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }

function getInv(player) {
  return player.getComponent("minecraft:inventory")?.container ?? null;
}

function consumeOneOfType(player, typeId) {
  const inv = getInv(player);
  if (!inv) return false;
  const sel = player.selectedSlotIndex;
  const cur = inv.getItem(sel);
  if (cur && cur.typeId === typeId) {
    if (cur.amount > 1) { cur.amount -= 1; inv.setItem(sel, cur); }
    else inv.setItem(sel, undefined);
    return true;
  }
  for (let i = 0; i < inv.size; i++) {
    const it = inv.getItem(i);
    if (it && it.typeId === typeId) {
      if (it.amount > 1) { it.amount -= 1; inv.setItem(i, it); }
      else inv.setItem(i, undefined);
      return true;
    }
  }
  return false;
}

function giveItem(player, typeId, count = 1) {
  const inv = getInv(player);
  if (!inv) return false;
  try {
    inv.addItem(new ItemStack(typeId, count));
    return true;
  } catch (_) {
    return false;
  }
}

// Block ids that don't block sky access — the plant grows around these like
// a vanilla tree pushes through leaves. Anything else above stops growth.
function isSkyTransparent(typeId) {
  if (!typeId) return true;
  if (typeId === CROP) return true; // ignore our own top half
  if (typeId === "minecraft:air") return true;
  // Glass / panes / bars
  if (typeId.includes("glass")) return true;
  if (typeId === "minecraft:iron_bars") return true;
  // Leaves and saplings (canopy-style obstructions)
  if (typeId.includes("leaves")) return true;
  if (typeId.includes("sapling")) return true;
  // Other plants / vines / hanging stuff
  if (typeId === "minecraft:vine" || typeId === "minecraft:weeping_vines"
      || typeId === "minecraft:twisting_vines") return true;
  return false;
}

function isAirAbove(dim, loc) {
  // Walk up from y+1; sky-transparent blocks don't count as obstructions.
  for (let dy = 1; dy < 64; dy++) {
    let b;
    try { b = dim.getBlock({ x: loc.x, y: loc.y + dy, z: loc.z }); } catch (_) { return true; }
    if (!b) return true;
    if (b.isAir || b.isLiquid) continue;
    if (isSkyTransparent(b.typeId)) continue;
    return false;
  }
  return true;
}

function findSunLampNear(dim, loc) {
  const r = SUN_LAMP_RANGE;
  for (let dy = -1; dy <= r; dy++) {
    for (let dx = -r; dx <= r; dx++) {
      for (let dz = -r; dz <= r; dz++) {
        let b;
        try { b = dim.getBlock({ x: loc.x + dx, y: loc.y + dy, z: loc.z + dz }); } catch (_) { continue; }
        if (b && b.typeId === SUN_LAMP) {
          try {
            if (b.permutation.getState("silverlabs:powered") === true) return true;
          } catch (_) {}
        }
      }
    }
  }
  return false;
}

function setAge(block, newAge, outdoor) {
  try {
    const perm = BlockPermutation.resolve(CROP, {
      [AGE]: clamp(newAge, 0, 5),
      [TOP]: false,
    });
    block.setPermutation(perm);
    if (outdoor && newAge >= 3) {
      // Place top half above for the visual "tall outdoor crop"
      const above = block.dimension.getBlock({ x: block.location.x, y: block.location.y + 1, z: block.location.z });
      if (above && above.isAir) {
        try {
          const topPerm = BlockPermutation.resolve(CROP, { [AGE]: clamp(newAge, 0, 5), [TOP]: true });
          above.setPermutation(topPerm);
        } catch (_) {}
      }
    }
  } catch (_) {}
}

function clearTopAbove(block) {
  const above = block.dimension.getBlock({ x: block.location.x, y: block.location.y + 1, z: block.location.z });
  if (above && above.typeId === CROP) {
    try { above.setType("minecraft:air"); } catch (_) {}
  }
}

// --- Growth: dense scan of an 11×11×5 box around each player ---
// Every hemp_crop base in range gets a roll on every tick — no random sampling.
system.runInterval(() => {
  for (const player of world.getAllPlayers()) {
    const dim = player.dimension;
    const px = Math.floor(player.location.x);
    const py = Math.floor(player.location.y);
    const pz = Math.floor(player.location.z);
    for (let dy = -SCAN_VERT; dy <= SCAN_VERT; dy++) {
      for (let dx = -SCAN_RADIUS; dx <= SCAN_RADIUS; dx++) {
        for (let dz = -SCAN_RADIUS; dz <= SCAN_RADIUS; dz++) {
          let b;
          try { b = dim.getBlock({ x: px + dx, y: py + dy, z: pz + dz }); } catch (_) { continue; }
          if (!b || b.typeId !== CROP) continue;
          if (b.permutation.getState(TOP) === true) continue; // only base ticks
          const age = b.permutation.getState(AGE) ?? 0;
          if (age >= 5) continue; // overripe stops
          const outdoor = isAirAbove(dim, b.location);
          let baseChance = 0;
          if (outdoor) baseChance = GROWTH_CHANCE_OUTDOOR;
          else if (findSunLampNear(dim, b.location)) baseChance = GROWTH_CHANCE_INDOOR;
          else continue;
          // Prime (age 4) lingers — slow the transition into overripe
          const c = age === 4 ? baseChance * OVERRIPE_CHANCE_MULT : baseChance;
          if (chance(c)) setAge(b, age + 1, outdoor);
        }
      }
    }
  }
}, GROWTH_INTERVAL_TICKS);

// --- Redstone power: scan known sources adjacent to a sun_lamp ---
const REDSTONE_FACES = [
  [1, 0, 0], [-1, 0, 0],
  [0, 1, 0], [0, -1, 0],
  [0, 0, 1], [0, 0, -1],
];
function isPowerSource(b) {
  if (!b) return false;
  const t = b.typeId;
  if (t === "minecraft:redstone_block") return true;
  if (t === "minecraft:lit_redstone_torch" || t === "minecraft:redstone_torch") {
    // torch lit state
    try { return b.permutation.getState("toggle_bit") !== false; } catch (_) {}
    return t === "minecraft:lit_redstone_torch";
  }
  if (t === "minecraft:powered_repeater") return true;
  if (t === "minecraft:powered_comparator") return true;
  if (t === "minecraft:lever") {
    try { return b.permutation.getState("open_bit") === true; } catch (_) { return false; }
  }
  if (t.endsWith("_button") || t.includes(":wooden_button") || t.includes(":stone_button")) {
    try { return b.permutation.getState("button_pressed_bit") === true; } catch (_) { return false; }
  }
  if (t === "minecraft:redstone_wire") {
    try {
      const p = b.permutation.getState("redstone_signal") ?? 0;
      return p > 0;
    } catch (_) { return false; }
  }
  if (t === "minecraft:daylight_detector_inverted") return true;
  return false;
}
function lampShouldBePowered(lamp) {
  // Try the script API redstone power first (newer servers expose it)
  try {
    const p = lamp.getRedstonePower();
    if (typeof p === "number" && p > 0) return true;
  } catch (_) {}
  const dim = lamp.dimension;
  for (const [dx, dy, dz] of REDSTONE_FACES) {
    let n;
    try { n = dim.getBlock({ x: lamp.location.x + dx, y: lamp.location.y + dy, z: lamp.location.z + dz }); } catch (_) { continue; }
    if (isPowerSource(n)) return true;
  }
  return false;
}

const LAMP_SCAN_INTERVAL_TICKS = 20; // 1s
system.runInterval(() => {
  for (const player of world.getAllPlayers()) {
    const dim = player.dimension;
    const px = Math.floor(player.location.x);
    const py = Math.floor(player.location.y);
    const pz = Math.floor(player.location.z);
    for (let dy = -SCAN_VERT; dy <= SCAN_VERT; dy++) {
      for (let dx = -SCAN_RADIUS; dx <= SCAN_RADIUS; dx++) {
        for (let dz = -SCAN_RADIUS; dz <= SCAN_RADIUS; dz++) {
          let b;
          try { b = dim.getBlock({ x: px + dx, y: py + dy, z: pz + dz }); } catch (_) { continue; }
          if (!b || b.typeId !== SUN_LAMP) continue;
          let cur = false;
          try { cur = b.permutation.getState("silverlabs:powered") === true; } catch (_) {}
          const want = lampShouldBePowered(b);
          if (cur !== want) {
            try {
              const perm = BlockPermutation.resolve(SUN_LAMP, { "silverlabs:powered": want });
              b.setPermutation(perm);
            } catch (_) {}
          }
        }
      }
    }
  }
}, LAMP_SCAN_INTERVAL_TICKS);

// --- Rabbit threat: damage outdoor hemp ---
system.runInterval(() => {
  for (const player of world.getAllPlayers()) {
    const dim = player.dimension;
    let rabbits;
    try {
      rabbits = dim.getEntities({ type: "minecraft:rabbit", location: player.location, maxDistance: 48 });
    } catch (_) { continue; }
    for (const rabbit of rabbits) {
      // For each rabbit, look at a small box around its feet for hemp_crop.
      const rx = Math.floor(rabbit.location.x);
      const ry = Math.floor(rabbit.location.y);
      const rz = Math.floor(rabbit.location.z);
      for (let dx = -1; dx <= 1; dx++) {
        for (let dz = -1; dz <= 1; dz++) {
          let b;
          try { b = dim.getBlock({ x: rx + dx, y: ry, z: rz + dz }); } catch (_) { continue; }
          if (!b || b.typeId !== CROP) continue;
          const isTop = b.permutation.getState(TOP) === true;
          if (isTop) continue;
          if (chance(0.35)) {
            const age = b.permutation.getState(AGE) ?? 0;
            if (age <= 0) {
              clearTopAbove(b);
              try { b.setType("minecraft:air"); } catch (_) {}
            } else {
              clearTopAbove(b);
              setAge(b, age - 1, isAirAbove(dim, b.location));
            }
            try { dim.runCommand(`particle minecraft:crit_particle ${rx + dx + 0.5} ${ry + 0.5} ${rz + dz + 0.5}`); } catch (_) {}
          }
        }
      }
    }
  }
}, RABBIT_INTERVAL_TICKS);

// --- Pillager raid threat: illagers carrying a banner steal hemp during raids ---
//     Gate on either an active raid (detected via a nearby ominous_banner) or a
//     pillager_captain so isolated tower pillagers don't constantly cull farms
//     in render distance. When triggered, drops the crop's age by 1 (or removes
//     it if age 0) and spawns 1 hemp_bud at the illager's feet so the player
//     can recover the loot by killing the raider.
const RAID_INTERVAL_TICKS = 100;
const ILLAGER_TYPES = new Set([
  "minecraft:pillager",
  "minecraft:vindicator",
  "minecraft:evocation_illager",
]);
const ILLAGER_HEMP_STEAL_CHANCE = 0.30;

function illagerIsRaiding(dim, ill) {
  // Cheapest: check if the illager is wearing/carrying an ominous_banner
  // (raid captain marker). Also accept any nearby ominous_banner block within
  // 8 blocks as evidence of an ongoing raid the illager is participating in.
  const equip = (() => { try { return ill.getComponent("minecraft:equippable"); } catch (_) { return null; } })();
  if (equip) {
    try {
      const head = equip.getEquipmentSlot("Head")?.getItem();
      if (head && head.typeId === "minecraft:ominous_banner") return true;
    } catch (_) {}
  }
  const ix = Math.floor(ill.location.x);
  const iy = Math.floor(ill.location.y);
  const iz = Math.floor(ill.location.z);
  for (let dx = -8; dx <= 8; dx += 4) {
    for (let dz = -8; dz <= 8; dz += 4) {
      let b;
      try { b = dim.getBlock({ x: ix + dx, y: iy, z: iz + dz }); } catch (_) { continue; }
      if (b && (b.typeId === "minecraft:standing_banner" || b.typeId === "minecraft:wall_banner")) {
        // Banners can be ominous via their NBT; we can't read it, so any banner
        // within 8 blocks of an illager is treated as raid evidence. Still rare.
        return true;
      }
    }
  }
  return false;
}

system.runInterval(() => {
  for (const player of world.getAllPlayers()) {
    const dim = player.dimension;
    let illagers;
    try {
      illagers = dim.getEntities({ families: [], location: player.location, maxDistance: 48 });
    } catch (_) { continue; }
    for (const ill of illagers) {
      if (!ILLAGER_TYPES.has(ill.typeId)) continue;
      if (!illagerIsRaiding(dim, ill)) continue;
      const ix = Math.floor(ill.location.x);
      const iy = Math.floor(ill.location.y);
      const iz = Math.floor(ill.location.z);
      for (let dx = -1; dx <= 1; dx++) {
        for (let dz = -1; dz <= 1; dz++) {
          let b;
          try { b = dim.getBlock({ x: ix + dx, y: iy, z: iz + dz }); } catch (_) { continue; }
          if (!b || b.typeId !== CROP) continue;
          if (b.permutation.getState(TOP) === true) continue;
          if (!chance(ILLAGER_HEMP_STEAL_CHANCE)) continue;
          const age = b.permutation.getState(AGE) ?? 0;
          const outdoor = isAirAbove(dim, b.location);
          if (age <= 0) {
            clearTopAbove(b);
            try { b.setType("minecraft:air"); } catch (_) {}
          } else {
            clearTopAbove(b);
            setAge(b, age - 1, outdoor);
          }
          // Drop a hemp_bud at the illager's feet — recoverable when killed.
          spawnDrop(dim, ill.location, BUD, 1);
          try { dim.runCommand(`particle minecraft:villager_angry ${ix + 0.5} ${iy + 1.0} ${iz + 0.5}`); } catch (_) {}
        }
      }
    }
  }
}, RAID_INTERVAL_TICKS);

// --- Wandering trader buyback: hand off hemp products for emeralds ---
//     Right-click a wandering_trader while holding hemp_bud / hemp_tincture /
//     hemp_seeds. Avoids overriding vanilla trade tables (which would pin us to
//     one Bedrock version) by acting as an off-menu interaction. Trader animates
//     a "happy villager" particle and the player's inventory swaps the items
//     for emeralds at the rates below.
const TRADER_BUYBACK = {
  [BUD]:      { perTrade: 2, emeralds: 1 },
  [TINCTURE]: { perTrade: 1, emeralds: 3 },
  [SEEDS]:    { perTrade: 8, emeralds: 1 },
};

world.beforeEvents.playerInteractWithEntity.subscribe((event) => {
  const target = event.target;
  if (!target || target.typeId !== "minecraft:wandering_trader") return;
  const stack = event.itemStack;
  if (!stack) return;
  const deal = TRADER_BUYBACK[stack.typeId];
  if (!deal) return;
  event.cancel = true; // suppress the vanilla trade window for this interaction
  const player = event.player;
  system.run(() => {
    // Count what the player has of this item across the inventory
    const inv = getInv(player);
    if (!inv) return;
    let have = 0;
    for (let i = 0; i < inv.size; i++) {
      const it = inv.getItem(i);
      if (it && it.typeId === stack.typeId) have += it.amount;
    }
    if (have < deal.perTrade) {
      player.sendMessage(`§7[Trader] Brings me at least §f${deal.perTrade}§7 of those and I'll pay you in emeralds.`);
      return;
    }
    const trades = Math.floor(have / deal.perTrade);
    let consumed = 0;
    for (let n = 0; n < trades * deal.perTrade; n++) {
      if (!consumeOneOfType(player, stack.typeId)) break;
      consumed++;
    }
    const actualTrades = Math.floor(consumed / deal.perTrade);
    if (actualTrades <= 0) return;
    giveItem(player, "minecraft:emerald", actualTrades * deal.emeralds);
    const loc = target.location;
    try { target.dimension.runCommand(`particle minecraft:villager_happy ${loc.x} ${loc.y + 1.5} ${loc.z}`); } catch (_) {}
    try { target.dimension.runCommand(`playsound mob.villager.yes @a ${loc.x} ${loc.y} ${loc.z} 0.8 1.1`); } catch (_) {}
    player.sendMessage(`§a[Trader] §fTraded §e${actualTrades * deal.perTrade}§f for §a${actualTrades * deal.emeralds} emerald${actualTrades * deal.emeralds === 1 ? "" : "s"}§f.`);
  });
});

// --- Outdoor detection on placement ---
world.afterEvents.playerPlaceBlock.subscribe((event) => {
  const block = event.block;
  if (!block || block.typeId !== CROP) return;
  // newly-placed crop is age 0 by default — we don't need to set anything,
  // outdoor status is computed live during ticks.
});

// --- Cleanup: breaking either half of a tall plant removes the other ---
//     Also: rare hemp_seeds drop from breaking vanilla grass / tall_grass /
//     short_grass — gives players a way to bootstrap into hemp without
//     already having seeds. Mirrors how vanilla wheat seeds work.
const GRASS_BLOCK_IDS = new Set([
  "minecraft:short_grass",
  "minecraft:tall_grass",
  "minecraft:fern",
  "minecraft:large_fern",
  // Pre-1.21 alias kept for safety on mixed-version worlds
  "minecraft:grass",
]);
const GRASS_HEMP_SEED_CHANCE = 0.04; // ~1 in 25 grass tufts

world.afterEvents.playerBreakBlock.subscribe((event) => {
  const brokenType = event.brokenBlockPermutation?.type?.id;

  // Hemp_crop tall-plant cleanup
  if (brokenType === CROP) {
    const block = event.block;
    const dim = block.dimension;
    const wasTop = event.brokenBlockPermutation.getState(TOP) === true;
    const dy = wasTop ? -1 : 1;
    let neighbor;
    try { neighbor = dim.getBlock({ x: block.location.x, y: block.location.y + dy, z: block.location.z }); } catch (_) { return; }
    if (!neighbor || neighbor.typeId !== CROP) return;
    const neighborIsTop = neighbor.permutation.getState(TOP) === true;
    if (wasTop === neighborIsTop) return;
    try { neighbor.setType("minecraft:air"); } catch (_) {}
    return;
  }

  // Hemp seed bootstrap from grass blocks
  if (brokenType && GRASS_BLOCK_IDS.has(brokenType)) {
    if (!chance(GRASS_HEMP_SEED_CHANCE)) return;
    const block = event.block;
    const loc = block.location;
    spawnDrop(block.dimension, loc, SEEDS, 1);
  }
});

// Per-player debounce so a single click that fires the event twice
// (a known Bedrock quirk for some interact paths) only does work once.
const recentInteract = new Map(); // key: playerId|x|y|z -> system.currentTick
function consumeInteractToken(player, block) {
  const key = `${player.id}|${block.location.x}|${block.location.y}|${block.location.z}`;
  const last = recentInteract.get(key) ?? -999;
  const now = system.currentTick;
  if (now - last < 4) return false; // <200ms — treat as duplicate
  recentInteract.set(key, now);
  // Lazy cleanup: prune any entries older than ~4s on each successful claim
  if (recentInteract.size > 64) {
    for (const [k, t] of recentInteract) {
      if (now - t > 80) recentInteract.delete(k);
    }
  }
  return true;
}

// --- Bonemeal on hemp_crop bumps growth ---
world.beforeEvents.playerInteractWithBlock.subscribe((event) => {
  const block = event.block;
  const stack = event.itemStack;
  const player = event.player;
  if (!block || block.typeId !== CROP) return;

  // Sheers harvest path
  if (stack && stack.typeId === "minecraft:shears") {
    event.cancel = true;
    if (!consumeInteractToken(player, block)) return;
    system.run(() => harvestWithShears(player, block, stack));
    return;
  }

  // Bone meal path
  if (stack && stack.typeId === "minecraft:bone_meal") {
    event.cancel = true;
    if (!consumeInteractToken(player, block)) return;
    system.run(() => {
      // If the player clicked the top half, redirect to the base
      let target = block;
      if (block.permutation.getState(TOP) === true) {
        const below = block.dimension.getBlock({ x: block.location.x, y: block.location.y - 1, z: block.location.z });
        if (below && below.typeId === CROP) target = below;
        else return;
      }
      const age = target.permutation.getState(AGE) ?? 0;
      // Cap bone meal at age 4 (prime) — overripe (5) only via neglect
      if (age >= 4) {
        player.sendMessage("§e[Hemp] Already mature — harvest with shears.");
        return;
      }
      // Always consume the bone meal, but only 50% chance to advance
      consumeOneOfType(player, "minecraft:bone_meal");
      const loc = target.location;
      try { target.dimension.runCommand(`particle minecraft:crop_growth_emitter ${loc.x + 0.5} ${loc.y + 0.5} ${loc.z + 0.5}`); } catch (_) {}
      if (!chance(0.5)) return;
      const outdoor = isAirAbove(target.dimension, loc);
      setAge(target, age + 1, outdoor);
      try { target.dimension.runCommand(`playsound random.fizz @a ${loc.x + 0.5} ${loc.y + 0.5} ${loc.z + 0.5} 0.6 1.6`); } catch (_) {}
    });
    return;
  }
});

function harvestWithShears(player, block, shearsStack) {
  if (!block || block.typeId !== CROP) return; // already harvested / replaced
  // Read state on the BASE block (not top)
  let base = block;
  if (block.permutation.getState(TOP) === true) {
    const below = block.dimension.getBlock({ x: block.location.x, y: block.location.y - 1, z: block.location.z });
    if (below && below.typeId === CROP) base = below;
  }
  const age = base.permutation.getState(AGE) ?? 0;
  const dim = base.dimension;
  const loc = base.location;
  const outdoor = isAirAbove(dim, loc);

  // Too early — leave the plant alone, don't damage shears
  if (age <= 1) {
    player.sendMessage("§7[Hemp] Too early — leave it to grow.");
    return;
  }

  const yields = computeYield(age, outdoor);
  if (yields.msg) player.sendMessage(yields.msg);

  if (yields.bud > 0) spawnDrop(dim, loc, BUD, yields.bud);
  if (yields.seeds > 0) spawnDrop(dim, loc, SEEDS, yields.seeds);

  // Fully remove the plant — player replants from seeds
  clearTopAbove(base);
  try { base.setType("minecraft:air"); } catch (_) {}

  damageShears(player, shearsStack);
  try { dim.runCommand(`playsound mob.sheep.shear @a ${loc.x + 0.5} ${loc.y + 0.5} ${loc.z + 0.5}`); } catch (_) {}
}

function computeYield(age, outdoor) {
  // Outdoor gets a bud bonus; indoor (sun-lamp grown) is baseline.
  const out = outdoor ? 1 : 0;
  switch (age) {
    case 2: return { bud: 1, seeds: 1 + out };
    case 3: return { bud: 2 + out, seeds: 1 + out };
    case 4: return { bud: 3 + out * 2, seeds: 1 + out, msg: "§a[Hemp] Prime harvest." };  // peak
    case 5: return { bud: rand(3), seeds: 3 + rand(3), msg: "§7[Hemp] Overripe — mostly seeds now." };
    default: return { bud: 0, seeds: 0 };
  }
}

function spawnDrop(dim, loc, typeId, count) {
  try {
    const it = new ItemStack(typeId, count);
    dim.spawnItem(it, { x: loc.x + 0.5, y: loc.y + 0.5, z: loc.z + 0.5 });
  } catch (_) {}
}

function damageShears(player, shearsStack) {
  try {
    const equippable = player.getComponent("minecraft:equippable");
    if (!equippable) return;
    const slot = equippable.getEquipmentSlot("Mainhand");
    if (!slot) return;
    const item = slot.getItem();
    if (!item || item.typeId !== "minecraft:shears") return;
    const dur = item.getComponent("minecraft:durability");
    if (!dur) return;
    const damage = dur.damage + 1;
    if (damage >= dur.maxDurability) {
      slot.setItem(undefined);
      try { player.dimension.runCommand(`playsound random.break @a ${player.location.x} ${player.location.y} ${player.location.z}`); } catch (_) {}
    } else {
      dur.damage = damage;
      slot.setItem(item);
    }
  } catch (_) {}
}

// --- Cauldron tincture brewing: hemp_bud + water cauldron + glass bottle => tincture ---
world.afterEvents.itemUse.subscribe((event) => {
  const player = event.source;
  const stack = event.itemStack;
  if (!stack || stack.typeId !== BUD) return;
  system.run(() => {
    const target = player.getBlockFromViewDirection({ maxDistance: 6 });
    const block = target?.block;
    if (!block) return;
    const isCauldron = block.typeId === "minecraft:cauldron" || block.typeId === "minecraft:water_cauldron";
    if (!isCauldron) return;

    // Need at least 3 buds total in inventory and 1 glass bottle
    const inv = getInv(player);
    if (!inv) return;
    let budCount = 0, bottleCount = 0;
    for (let i = 0; i < inv.size; i++) {
      const it = inv.getItem(i);
      if (!it) continue;
      if (it.typeId === BUD) budCount += it.amount;
      else if (it.typeId === "minecraft:glass_bottle") bottleCount += it.amount;
    }
    if (budCount < 3) {
      player.sendMessage("§c[Hemp] Need 3 hemp buds to brew tincture.");
      return;
    }
    if (bottleCount < 1) {
      player.sendMessage("§c[Hemp] Need an empty glass bottle.");
      return;
    }

    // Check water level (water cauldron has fill_level state 1-3; vanilla cauldron = empty)
    let level = 0;
    try { level = block.permutation.getState("fill_level") ?? 0; } catch (_) {}
    if (block.typeId === "minecraft:cauldron") {
      player.sendMessage("§c[Hemp] Cauldron must contain water.");
      return;
    }
    if (level <= 0) {
      player.sendMessage("§c[Hemp] Cauldron is empty.");
      return;
    }

    // Consume 3 buds, 1 bottle
    for (let i = 0; i < 3; i++) consumeOneOfType(player, BUD);
    consumeOneOfType(player, "minecraft:glass_bottle");

    // Decrement water level
    try {
      const newLevel = level - 1;
      if (newLevel <= 0) {
        block.setType("minecraft:cauldron");
      } else {
        const perm = BlockPermutation.resolve("minecraft:water_cauldron", { fill_level: newLevel });
        block.setPermutation(perm);
      }
    } catch (_) {}

    // Give tincture
    giveItem(player, TINCTURE, 1);
    const loc = block.location;
    try { block.dimension.runCommand(`particle minecraft:water_splash_particle_manual ${loc.x + 0.5} ${loc.y + 1.0} ${loc.z + 0.5}`); } catch (_) {}
    try { block.dimension.runCommand(`playsound bucket.fill_water @a ${loc.x + 0.5} ${loc.y + 0.5} ${loc.z + 0.5}`); } catch (_) {}
    player.sendMessage("§a[Hemp] Hemp tincture brewed.");
  });
});

// --- Consumption effects ---
world.afterEvents.itemCompleteUse.subscribe((event) => {
  const player = event.source;
  const stack = event.itemStack;
  if (!stack) return;
  if (stack.typeId === TINCTURE) {
    try {
      player.addEffect("regeneration", 100, { amplifier: 1, showParticles: true });
      player.addEffect("slowness", 200, { amplifier: 0, showParticles: true });
    } catch (_) {}
    player.sendMessage("§a[Hemp] You feel a warm, calming wave.");
  } else if (stack.typeId === BROWNIE) {
    try {
      player.addEffect("regeneration", 200, { amplifier: 0, showParticles: true });
      player.addEffect("slowness", 400, { amplifier: 0, showParticles: true });
    } catch (_) {}
    player.sendMessage("§a[Hemp] Mmm. You feel relaxed.");
  }
});

system.run(() => {
  world.sendMessage("§a[Hemp] §7Hemp pack loaded.");
});
