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

// Read-only peek into the private-chest addon's world dynamic property.
const PRIVATE_CHESTS_PROP = "private_chests_v1";

function loadRegistry() {
  try {
    const raw = world.getDynamicProperty(PRIVATE_CHESTS_PROP);
    if (raw && typeof raw === "string") return JSON.parse(raw);
  } catch (_) {}
  return {};
}

// Returns [{ container, label }, ...] for every reachable private chest owned by player.
// Chests in unloaded chunks or that no longer exist are silently skipped.
export function readOwnedChests(player) {
  const registry = loadRegistry();
  const sources = [];
  const skipped = [];

  for (const [key, entry] of Object.entries(registry)) {
    if (!entry || entry.ownerId !== player.id) continue;
    const parts = key.split(",");
    if (parts.length < 4) continue;
    const x = parseInt(parts[0], 10);
    const y = parseInt(parts[1], 10);
    const z = parseInt(parts[2], 10);
    const dimId = parts.slice(3).join(",");

    let dim;
    try { dim = world.getDimension(dimId); } catch (_) { continue; }

    let block;
    try { block = dim.getBlock({ x, y, z }); } catch (_) { block = null; }
    if (!block) { skipped.push({ x, y, z, dim: dimId }); continue; }
    if (block.typeId !== "minecraft:chest") continue;

    let inv;
    try { inv = block.getComponent("inventory"); } catch (_) { continue; }
    const container = inv?.container;
    if (!container) continue;

    sources.push({ container, label: `${x},${y},${z}` });
  }

  return { sources, skipped };
}

// Build a Map<typeId, totalCount> across the player inventory and chest containers.
export function tallyItems(playerInv, chestSources) {
  const totals = new Map();

  const addContainer = (c) => {
    if (!c) return;
    for (let i = 0; i < c.size; i++) {
      const item = c.getItem(i);
      if (!item) continue;
      totals.set(item.typeId, (totals.get(item.typeId) || 0) + item.amount);
    }
  };

  addContainer(playerInv);
  for (const src of chestSources) addContainer(src.container);

  return totals;
}

// How many times can a recipe be crafted given the totals map.
export function craftableCount(recipe, totals) {
  let min = Infinity;
  for (const ing of recipe.ingredients) {
    let available = 0;
    for (const typeId of ing.any) available += totals.get(typeId) || 0;
    const times = Math.floor(available / ing.count);
    if (times < min) min = times;
    if (min === 0) return 0;
  }
  return min === Infinity ? 0 : min;
}

// Remove `count` items matching any of `typeIds` from the sources in order.
// Returns the number actually removed (caller should verify == count).
// `sources` is an ordered list of containers: typically [playerInv, ...chestContainers].
function drain(typeIds, count, sources) {
  let remaining = count;
  for (const container of sources) {
    if (!container) continue;
    for (let i = 0; i < container.size && remaining > 0; i++) {
      const item = container.getItem(i);
      if (!item) continue;
      if (!typeIds.includes(item.typeId)) continue;
      if (item.amount <= remaining) {
        remaining -= item.amount;
        container.setItem(i, undefined);
      } else {
        const clone = item.clone();
        clone.amount = item.amount - remaining;
        container.setItem(i, clone);
        remaining = 0;
      }
    }
    if (remaining === 0) break;
  }
  return count - remaining;
}

// Consume ingredients for a single craft. Returns true on success, false if anything
// was short (in which case nothing has been consumed — caller should pre-verify).
export function consumeIngredients(recipe, playerInv, chestSources) {
  const containers = [playerInv, ...chestSources.map((s) => s.container)];
  for (const ing of recipe.ingredients) {
    const removed = drain(ing.any, ing.count, containers);
    if (removed < ing.count) return false;
  }
  return true;
}
