// ---- 02_unlocks_rewards.js ----
// guardian_system source chunk
// Original lines: 2206-2959

function getClasseInfo(player) {
  return rjGetPlayerClassInfo(player);
}

function setClasse(player, classe) {
  ensureObjective(OBJ_CLASSE, "Classe");
  const obj = world.scoreboard.getObjective(OBJ_CLASSE);

  obj.setScore(player, classe.valor);
  rjSetPlayerClassTags(player, classe);
}

function isCreative(player) {
  try {
    // APIs variam por versão: em algumas builds é string ("creative"), em outras é enum.
    const gm = player.getGameMode ? player.getGameMode() : player.gameMode;
    return gm === "creative" || (gm && String(gm).toLowerCase() === "creative");
  } catch (e) {
    return false;
  }
}

function isApprentice(player) {
  try { return !!player?.hasTag?.(TAG_APPRENTICE); } catch (e) { return false; }
}

function hasFixedClass(player) {
  try { return !!player?.hasTag?.(TAG_FIXED_CLASS); } catch (e) { return false; }
}

function ensureApprentice(player) {
  try { player?.addTag?.(TAG_APPRENTICE); } catch (e) {}
}

function canFixCurrentClass(player) {
  return isApprentice(player) && !!getClasseInfo(player) && !hasFixedClass(player);
}

function fixCurrentClass(player) {
  const classe = getClasseInfo(player);
  if (!classe) return false;
  ensureApprentice(player);
  try { player.addTag?.(TAG_FIXED_CLASS); } catch (e) {}
  return true;
}

function _safeOverworldReturnPos(player) {
  try {
    const sp = player?.getSpawnPoint?.();
    if (sp && Number.isFinite(sp.x) && Number.isFinite(sp.y) && Number.isFinite(sp.z)) return { x: sp.x, y: sp.y, z: sp.z };
  } catch (e) {}
  return { x: 0, y: 80, z: 0 };
}

function enforceDimensionLockForUnfixedPlayer(player) {
  try {
    if (!player || isCreative(player) || hasFixedClass(player)) return;
    const dimId = String(player.dimension?.id ?? "");
    if (dimId === "minecraft:overworld" || dimId === "overworld") return;

    const overworld = world.getDimension("overworld");
    const pos = _safeOverworldReturnPos(player);
    try {
      player.teleport(pos, { dimension: overworld, keepVelocity: false });
    } catch (e) {
      try { player.teleport(pos, { dimension: overworld }); } catch (e2) {}
    }

    if (!player.hasTag?.(TAG_DIMENSION_WARN)) {
      try { player.addTag?.(TAG_DIMENSION_WARN); } catch (e) {}
      system.run(() => {
        try { __LWJ_MUTE_MESSAGE__(player, _gUi("msg.dimension_locked_until_fixed")); } catch (e) {}
      });
      system.runTimeout(() => { try { player.removeTag?.(TAG_DIMENSION_WARN); } catch (e) {} }, 80);
    }
  } catch (e) {}
}

function resetClasse(player) {
  try { player.clearEffects(); } catch(e) {}
  // Remove tags de efeitos permanentes do NPC
  try {
    for (const t of player.getTags()) {
      if (typeof t === "string" && (t.startsWith(TAG_EFF_PREFIX) || t.startsWith(TAG_EFF_DISABLED_PREFIX))) {
        try { player.removeTag(t); } catch(e) {}
      }
    }
  } catch(e) {}
  // zera scoreboard (se existir) e remove tags relacionadas à classe
  try {
    const obj = ensureObjective(OBJ_CLASSE, "Classe");
    obj.setScore(player, 0);
  } catch (e) {}

  try { rjClearPlayerClassTags(player, { removeSelected: true }); } catch (e) {}

  // remove marcação do manual (para receber novamente após reset)
  for (const c of CLASSES) {
    try { player.removeTag(`${TAG_MANUAL_PREFIX}${c.id}`); } catch (e) {}
  }
  try { player.removeTag(TAG_CAMINHO_PRINCIPAL); } catch (e) {}
  try { player.removeTag(TAG_APPRENTICE); } catch (e) {}
  try { player.removeTag(TAG_FIXED_CLASS); } catch (e) {}
  for (const t of [...(CG.questTags.warriorDone ?? []), ...(CG.questTags.archerDone ?? [])]) {
    try { player.removeTag(t); } catch (e) {}
  }
  try { rjClearPlayerClassCache(player); } catch (e) {}
}

function getContainer(player) {
  return _getInventoryContainerSafe(player) ?? null;
}

function addItem(player, typeId, amount) {
  const c = getContainer(player);
  if (!c) return false;
  const stack = new ItemStack(typeId, amount);
  const leftover = c.addItem(stack);
  // addItem returns ItemStack if leftover couldn't be added (engine differs). We'll treat truthy as partial.
  return leftover ? false : true;
}

function addItemStack(player, stack) {
  const c = getContainer(player);
  if (!c) return false;
  const leftover = c.addItem(stack);
  return leftover ? false : true;
}

function removeItems(player, typeId, amount) {
  const c = getContainer(player);
  if (!c) return false;

  let toRemove = amount;
  for (let i = 0; i < c.size; i++) {
    const it = c.getItem(i);
    if (!it) continue;
    if (it.typeId !== typeId) continue;

    const take = Math.min(it.amount, toRemove);
    it.amount -= take;
    toRemove -= take;

    if (it.amount <= 0) c.setItem(i, undefined);
    else c.setItem(i, it);

    if (toRemove <= 0) break;
  }
  return toRemove <= 0;
}

function countItems(player, typeId) {
  const c = getContainer(player);
  if (!c) return 0;
  let n = 0;
  for (let i = 0; i < c.size; i++) {
    const it = c.getItem(i);
    if (it && it.typeId === typeId) n += it.amount;
  }
  return n;
}

function countAnyPlanks(player) {
  const c = getContainer(player);
  if (!c) return 0;
  let total = 0;
  for (let i = 0; i < c.size; i++) {
    const it = c.getItem(i);
    if (!it) continue;
    const id = it.typeId;
    if (typeof id === "string" && id.startsWith("minecraft:") && id.endsWith("_planks")) {
      total += it.amount;
    }
  }
  return total;
}

function removeAnyPlanks(player, amount) {
  const c = getContainer(player);
  if (!c) return false;
  let left = amount;
  for (let i = 0; i < c.size; i++) {
    const it = c.getItem(i);
    if (!it) continue;
    const id = it.typeId;
    if (!(typeof id === "string" && id.startsWith("minecraft:") && id.endsWith("_planks"))) continue;

    const take = Math.min(it.amount, left);
    if (take <= 0) continue;

    if (it.amount === take) c.setItem(i, undefined);
    else {
      it.amount -= take;
      c.setItem(i, it);
    }
    left -= take;
    if (left <= 0) return true;
  }
  return left <= 0;
}

function giveUnlockXP_MainOnly(player) {
  // Class confirmation keeps its intended XP reward. This is not starter gear,
  // not an automatic effect, and not a passive class advantage.
  try { return awardExactQuestLevel(player, 1); } catch (e) { return false; }
}

function equipGreenLeatherSet(_player) {
  // Sistema de armadura de couro tingida removido.
  return false;
}
function equipLeatherChestplate(player) {
  tryReturnOrDrop(player, new ItemStack("minecraft:leather_chestplate", 1));
  return true;
}

function equipIronBoots(player) {
  tryReturnOrDrop(player, new ItemStack("minecraft:iron_boots", 1));
  return true;
}

function equipIronLeggings(player) {
  tryReturnOrDrop(player, new ItemStack("minecraft:iron_leggings", 1));
  return true;
}

function equipIronHelmet(player) {
  tryReturnOrDrop(player, new ItemStack("minecraft:iron_helmet", 1));
  return true;
}

function equipLeatherPiece(player, itemId) {
  try {
    if (typeof itemId !== "string") return false;
    const it = new ItemStack(itemId, 1);
    tryReturnOrDrop(player, it);
    return true;
  } catch(e) { return false; }
}

function getArmorSlotForItemId(itemId) {
  const slotMap = {
    "minecraft:leather_boots": EquipmentSlot.Feet,
    "minecraft:chainmail_boots": EquipmentSlot.Feet,
    "minecraft:golden_boots": EquipmentSlot.Feet,
    "minecraft:iron_boots": EquipmentSlot.Feet,
    "minecraft:diamond_boots": EquipmentSlot.Feet,
    "minecraft:netherite_boots": EquipmentSlot.Feet,
    "minecraft:leather_leggings": EquipmentSlot.Legs,
    "minecraft:chainmail_leggings": EquipmentSlot.Legs,
    "minecraft:golden_leggings": EquipmentSlot.Legs,
    "minecraft:iron_leggings": EquipmentSlot.Legs,
    "minecraft:diamond_leggings": EquipmentSlot.Legs,
    "minecraft:netherite_leggings": EquipmentSlot.Legs,
    "minecraft:leather_chestplate": EquipmentSlot.Chest,
    "minecraft:chainmail_chestplate": EquipmentSlot.Chest,
    "minecraft:golden_chestplate": EquipmentSlot.Chest,
    "minecraft:iron_chestplate": EquipmentSlot.Chest,
    "minecraft:diamond_chestplate": EquipmentSlot.Chest,
    "minecraft:netherite_chestplate": EquipmentSlot.Chest,
    "minecraft:leather_helmet": EquipmentSlot.Head,
    "minecraft:chainmail_helmet": EquipmentSlot.Head,
    "minecraft:golden_helmet": EquipmentSlot.Head,
    "minecraft:iron_helmet": EquipmentSlot.Head,
    "minecraft:diamond_helmet": EquipmentSlot.Head,
    "minecraft:netherite_helmet": EquipmentSlot.Head,
  };
  return slotMap[itemId];
}

const ARMOR_REWARD_RECIPES = {
  "minecraft:leather_helmet": [["minecraft:leather", 5]],
  "minecraft:leather_chestplate": [["minecraft:leather", 8]],
  "minecraft:leather_leggings": [["minecraft:leather", 7]],
  "minecraft:leather_boots": [["minecraft:leather", 4]],
  "minecraft:chainmail_helmet": [["minecraft:iron_ingot", 5]],
  "minecraft:chainmail_chestplate": [["minecraft:iron_ingot", 8]],
  "minecraft:chainmail_leggings": [["minecraft:iron_ingot", 7]],
  "minecraft:chainmail_boots": [["minecraft:iron_ingot", 4]],
  "minecraft:golden_helmet": [["minecraft:gold_ingot", 5]],
  "minecraft:golden_chestplate": [["minecraft:gold_ingot", 8]],
  "minecraft:golden_leggings": [["minecraft:gold_ingot", 7]],
  "minecraft:golden_boots": [["minecraft:gold_ingot", 4]],
  "minecraft:iron_helmet": [["minecraft:iron_ingot", 5]],
  "minecraft:iron_chestplate": [["minecraft:iron_ingot", 8]],
  "minecraft:iron_leggings": [["minecraft:iron_ingot", 7]],
  "minecraft:iron_boots": [["minecraft:iron_ingot", 4]],
  "minecraft:diamond_helmet": [["minecraft:diamond", 5]],
  "minecraft:diamond_chestplate": [["minecraft:diamond", 8]],
  "minecraft:diamond_leggings": [["minecraft:diamond", 7]],
  "minecraft:diamond_boots": [["minecraft:diamond", 4]],
  "minecraft:netherite_helmet": [["minecraft:netherite_ingot", 5]],
  "minecraft:netherite_chestplate": [["minecraft:netherite_ingot", 8]],
  "minecraft:netherite_leggings": [["minecraft:netherite_ingot", 7]],
  "minecraft:netherite_boots": [["minecraft:netherite_ingot", 4]],
};

function getArmorRewardRecipe(itemId) {
  const recipe = ARMOR_REWARD_RECIPES[itemId];
  return Array.isArray(recipe) ? recipe : null;
}

function giveArmorRewardItem(player, itemId) {
  try {
    const stack = new ItemStack(itemId, 1);
    tryReturnOrDrop(player, stack);
    return "stored";
  } catch (e) {
    return "failed";
  }
}

function giveArmorRewardMaterials(player, itemId) {
  const recipe = getArmorRewardRecipe(itemId);
  if (!recipe?.length) return false;
  let ok = true;
  for (const [typeId, amount] of recipe) {
    try {
      tryReturnOrDrop(player, new ItemStack(typeId, Math.max(1, Number(amount) || 1)));
    } catch (e) {
      ok = false;
    }
  }
  return ok;
}

function formatArmorRewardMaterialsRaw(itemId) {
  const recipe = getArmorRewardRecipe(itemId);
  if (!recipe?.length) return getQuestItemLabelRaw(itemId);
  const parts = [];
  for (const [typeId, amount] of recipe) {
    if (parts.length) parts.push(" + ");
    parts.push(`${Math.max(1, Number(amount) || 1)}x `, getQuestItemLabelRaw(typeId));
  }
  return _rmJoin(...parts);
}


function equipOffhandShield(player) {
  // Auto-equip de mão secundária desativado para manter o inventário sob controle do jogador.
  return false;
}

function giveMainArmor(player, classeId) {
  // Recompensa extra apenas no caminho principal.
  // Nota: o kit padrão já entrega o essencial; aqui ficam apenas "extras" leves.
  if (classeId === CG.classes.warrior) {
    // (Hotfix) Guerreiro não recebe mais set completo aqui.
    // A recompensa do Guerreiro foi ajustada no kit (capacete + espada + escudo + 1 nível).
    return;
  }

  if (classeId === CG.classes.archer) {
    // O Arqueiro agora segue a mesma lógica do Guerreiro: kit base na escolha
    // e peças adicionais liberadas em etapas pela trilha de missões.
    return;
  }

  // mago: sem recompensas de armadura
  return;
}

function awardExactQuestLevel(player, levels = 1) {
  const amount = Math.max(1, Number(levels) || 1);
  try {
    if (typeof player.addLevels === "function") {
      player.addLevels(amount);
      return true;
    }
  } catch (e) {}

  try {
    if (typeof player.addExperience === "function") {
      let remainingLevels = amount;
      while (remainingLevels > 0) {
        const needed = Math.max(1, Number(player.totalXpNeededForNextLevel ?? 0) - Number(player.xpEarnedAtCurrentLevel ?? 0));
        player.addExperience(needed);
        remainingLevels--;
      }
      return true;
    }
  } catch (e) {}

  return false;
}

function giveStepXP(player) {
  // Só dá XP de etapa no caminho principal.
  try { if (!player.hasTag(TAG_CAMINHO_PRINCIPAL)) return; } catch (e) { return; }
  // +1 nível por etapa concluída (pós-classe)
  if (typeof player.addLevels === "function") {
    player.addLevels(1);
    return;
  }
  if (typeof player.addExperience === "function") {
    // Fallback: calcula o restante até o próximo nível quando addLevels não existir
    awardExactQuestLevel(player, 1);
  }
}


function makeEnchantedBook(enchId, lvl) {
  const book = new ItemStack("minecraft:enchanted_book", 1);
  const ench = book.getComponent("minecraft:enchantable");
  if (ench && EnchantmentTypes && typeof EnchantmentTypes.get === "function") {
    const t = EnchantmentTypes.get(String(enchId ?? ""));
    if (t) ench.addEnchantment({ type: t, level: Math.max(1, Number(lvl ?? 1)) });
  }
  return book;
}

function makeMendingBook() {
  const book = new ItemStack("minecraft:enchanted_book", 1);
  const ench = book.getComponent("minecraft:enchantable");
  if (ench && EnchantmentTypes && typeof EnchantmentTypes.get === "function") {
    const mend = EnchantmentTypes.get("mending");
    if (mend) ench.addEnchantment({ type: mend, level: 1 });
  }
  return book;
}

// ====== Kits de início de classe ======
function giveKit(player, classeId) {
  if (classeId === CG.classes.warrior) {
    // Recompensas entregues no inventário/drop; nada é equipado automaticamente.
    tryReturnOrDrop(player, new ItemStack("minecraft:iron_helmet", 1));
    tryReturnOrDrop(player, new ItemStack("minecraft:iron_sword", 1));
    tryReturnOrDrop(player, new ItemStack("minecraft:shield", 1));
    tryReturnOrDrop(player, new ItemStack("minecraft:bread", 8));
    try { awardExactQuestLevel(player, 1); } catch (e) {}
    return;
  }
  if (classeId === CG.classes.archer) {
    try { awardExactQuestLevel(player, 1); } catch (e) {}
    // Capacete de couro normal: entregue no inventário/drop, sem tingimento e sem auto-equip.
    tryReturnOrDrop(player, new ItemStack("minecraft:leather_helmet", 1));
    tryReturnOrDrop(player, new ItemStack("minecraft:bow", 1));
    tryReturnOrDrop(player, new ItemStack("minecraft:arrow", 24));
    tryReturnOrDrop(player, new ItemStack("minecraft:bread", 8));
    return;
  }
  if (classeId === CG.classes.mage) {
    try { awardExactQuestLevel(player, 1); } catch (e) {}
    tryReturnOrDrop(player, new ItemStack("minecraft:trident", 1));
    tryReturnOrDrop(player, new ItemStack("minecraft:golden_apple", 4));
    tryReturnOrDrop(player, new ItemStack("minecraft:bread", 8));
  }
}

// ====== Desbloqueio pré-classe (ingredientes) ======

function reqLabelRaw(r) {
  const tipo = String(r?.tipo ?? "");
  const map = {
    "minecraft:iron_ingot": _gUi("item.iron_ingot"),
    "minecraft:gold_ingot": _gUi("item.gold_ingot"),
    "minecraft:gold_nugget": _gUi("item.gold_nugget"),
    "minecraft:diamond": _gUi("item.diamond"),
    "minecraft:apple": _gUi("item.apple"),
    "minecraft:wheat": _gUi("item.wheat"),
    "minecraft:stick": _gUi("item.stick"),
    "minecraft:leather": _gUi("item.leather"),
    "minecraft:flint": _gUi("item.flint"),
    "minecraft:feather": _gUi("item.feather"),
    "minecraft:string": _gUi("item.string"),
    "minecraft:cobblestone": _gUi("item.cobblestone"),
    "minecraft:redstone": _gUi("item.redstone"),
    "minecraft:lapis_lazuli": _gUi("item.lapis_lazuli"),
    "ANY_PLANK": _gUi("item.any_plank"),
    "ANY_LOG": _gUi("item.any_log"),
  };
  return map[tipo] ?? _gUi("item.unknown");
}
function requisitosMain(classeId) {
  if (classeId === CG.classes.warrior) {
    return [
      { tipo: "minecraft:iron_ingot", q: 8 },
          { tipo: "minecraft:stick", q: 1 },
          { tipo: "ANY_PLANK", q: 6 },
    ];
  }

    if (classeId === CG.classes.archer) {
    return [
      { tipo: "minecraft:flint", q: 6 },
      { tipo: "minecraft:stick", q: 8 },
      { tipo: "minecraft:feather", q: 6 },
      { tipo: "minecraft:string", q: 3 },
      { tipo: "ANY_PLANK", q: 3 },
    ];
  }
// mago
  return [
    { tipo: "minecraft:gold_ingot", q: 16 },
    { tipo: "minecraft:gold_nugget", q: 16 },
    { tipo: "minecraft:diamond", q: 3 },
    { tipo: "minecraft:stick", q: 1 },
    { tipo: "minecraft:apple", q: 4 },
  ];
}

function requisitosAlt(classeId) {
  if (classeId === CG.classes.warrior) {
    return [
      { tipo: "minecraft:cobblestone", q: 32 },
          { tipo: "ANY_LOG", q: 8 },
    ];
  }
return [
{ tipo: "minecraft:redstone", q: 16 },
    { tipo: "minecraft:lapis_lazuli", q: 16 },
  ];
}

const LOG_IDS = [
  "minecraft:oak_log","minecraft:spruce_log","minecraft:birch_log","minecraft:jungle_log","minecraft:acacia_log",
  "minecraft:dark_oak_log","minecraft:mangrove_log","minecraft:cherry_log","minecraft:bamboo_block",
  "minecraft:crimson_stem","minecraft:warped_stem"
];

function countAnyLogs(player) {
  let total = 0;
  for (const id of LOG_IDS) total += countItems(player, id);
  return total;
}

function removeAnyLogs(player, amount) {
  let left = amount;
  for (const id of LOG_IDS) {
    const have = countItems(player, id);
    if (have <= 0) continue;
    const take = Math.min(have, left);
    if (take > 0) {
      removeItems(player, id, take);
      left -= take;
      if (left <= 0) return true;
    }
  }
  return left <= 0;
}

function faltasTexto(player, reqs) {
  const faltas = [];
  for (const r of reqs) {
    let have = 0;
    if (r.tipo === "ANY_PLANK") have = countAnyPlanks(player);
    else if (r.tipo === "ANY_LOG") have = countAnyLogs(player);
    else have = countItems(player, r.tipo);

    if (have < r.q) {
      faltas.push(_rmJoin("- ", reqLabelRaw(r), _rmText(`: ${have}/${r.q}`)));
    }
  }
  if (!faltas.length) return null;
  const raw = { rawtext: [] };
  faltas.forEach((entry, idx) => {
    if (idx > 0) raw.rawtext.push({ text: "\n" });
    if (entry?.rawtext) raw.rawtext.push(...entry.rawtext);
    else raw.rawtext.push(entry);
  });
  return raw;
}

function consumeReqs(player, reqs) {
  // primeiro verifica tudo
  for (const r of reqs) {
    let have = 0;
    if (r.tipo === "ANY_PLANK") have = countAnyPlanks(player);
    else if (r.tipo === "ANY_LOG") have = countAnyLogs(player);
    else have = countItems(player, r.tipo);
    if (have < r.q) return false;
  }
  // depois remove
  for (const r of reqs) {
    if (r.tipo === "ANY_PLANK") removeAnyPlanks(player, r.q);
    else if (r.tipo === "ANY_LOG") removeAnyLogs(player, r.q);
    else removeItems(player, r.tipo, r.q);
  }
  return true;
}

async function desbloquearClasse(player, classe, modo) {
  // modo: "main" | "alt"
  const reqs = modo === "main" ? requisitosMain(classe.id) : requisitosAlt(classe.id);
  const faltas = faltasTexto(player, reqs);
  if (faltas) {
    _showFormSafe(new MessageFormData()
      .title(_rmJoin(_rmText(`${classe.emoji} `), _gClassRaw(classe), _rmText(" — "), _gUi("requirements.title_suffix")))
      .body(_rmJoin(_gUi("requirements.missing_prefix"), "\n", faltas))
      .button1(_rmTranslate("lwj.ui.common.ok"))
      .button2(_rmTranslate("lwj.ui.common.close")), player);
    return;
  }

  const ok = consumeReqs(player, reqs);
  if (!ok) return;

  ensureApprentice(player);
  setClasse(player, classe);
  // Marca qual caminho de desbloqueio o jogador escolheu (para ajustes de recompensas).
  try {
    if (modo === "main") player.addTag(TAG_CAMINHO_PRINCIPAL);
    else player.removeTag(TAG_CAMINHO_PRINCIPAL);
  } catch (e) {}
  // Class unlocks intentionally do not grant starter gear, free items, or automatic effects.
  // The class confirmation XP reward remains enabled and separate from starter bonuses.
  try { giveUnlockXP_MainOnly(player); } catch (e) {}

  // manual da classe (livro)
  giveManualBook(player, classe.id);

  // efeitos de recompensa por classe
  // (v0.0.7) Efeitos infinitos agora são desbloqueados por troca no Suporte de Poções.
  // void grantClasseRewardEffects(player, classe.id);

  _gSend(player, _gUi("msg.class_unlocked_prefix"), _gClassRaw(classe), _gUi("msg.class_unlocked_suffix"));
}

