import { world, system, Player, EasingType, EntityDamageCause, ItemStack, BlockPermutation } from "@minecraft/server";
import { ActionFormData, ModalFormData } from "@minecraft/server-ui";
import { addLearnPoints, cacheLearnPoints } from "./utils/learnPoints.js";


// --- Flying Pokédex helper ---
function __isFlyablePokemon(entityTypeId) {
  try {
    const tid = String(entityTypeId || "");
    const pdata = pokemonsData.find(p => p?.id === tid);
    const types = Array.isArray(pdata?.types) ? pdata.types.map(t => String(t).toLowerCase()) : [];
    const canFlyFlag = Boolean(pdata?.canFly || pdata?.flyable || pdata?.can_fly);
    const inTypes = types.includes("flying");
    const inOverride = Array.isArray(flyableOverrideIds) && flyableOverrideIds.includes(tid);
    return inTypes || canFlyFlag || inOverride;
  } catch { return false; }
}


// --- Safe UI helpers (restricted execution safe) ---
function __escapeJsonText(s) {
  return String(s).replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
}
function __safeActionBar(p, msg) {
  // Some events (e.g., chat) run in restricted execution where native UI calls are blocked.
  // Defer to next tick; fall back to titleraw actionbar.
  try {
    system.run(() => {
      try {
        p?.onScreenDisplay?.setActionBar?.(String(msg));
      } catch (e) {
        try {
          const t = __escapeJsonText(msg);
          p?.runCommandAsync?.(`titleraw @s actionbar {"rawtext":[{"text":"${t}"}]}`);
        } catch (_) {}
      }
    });
  } catch (e) {
    try {
      const t = __escapeJsonText(msg);
      p?.runCommandAsync?.(`titleraw @s actionbar {"rawtext":[{"text":"${t}"}]}`);
    } catch (_) {}
  }
}
// --- End safe UI helpers ---
import "./imports/menus.js"
import "./imports/crates.js"

import { pokemonsData } from "./imports/data.js"
import { flyableOverrideIds } from "./imports/flyable_override.js"
import { pokemonCanLearn } from "./imports/pokemonCanLearn.js";
import { MovesData } from "./imports/moves.js";


import { starterMenu, introMenu } from "./data/ui/starterMenu.js";
import { battleMenu, formatEntityName2, dailyMenu, sendOutMenu, formatAttackName, setMovesOnSpawn } from "./imports/menus.js"
import { pokemonUtils } from "./utils/pokemonUtils.js";


// --- Mount AI Lock (prevents mounts from "fighting" rider input) ---
// Some pokemon have follow/wander/target behaviors that continue to issue navigation commands while being ridden.
// We hard-stop navigation frequently while a player is riding the pokemon.
// This is lightweight: we only scan near each player.
system.runInterval(() => {
  try {
    for (const p of world.getAllPlayers()) {
      const dim = p.dimension;
      let nearby = [];
      try {
        nearby = dim.getEntities({ location: p.location, maxDistance: 48 });
      } catch {
        try { nearby = dim.getEntities(); } catch { nearby = []; }
      }

      for (const e of nearby) {
        try {
          if (!e?.typeId || typeof e.typeId !== "string" || !e.typeId.startsWith("pr:")) continue;

          const rideable = e.getComponent?.("minecraft:rideable");
          if (!rideable) continue;

          const riders = (rideable.getRiders?.() ?? rideable.riders ?? []);
          if (!riders || riders.length === 0) continue;

          // If any rider is a player, cancel AI pathing.
          const hasPlayerRider = riders.some(r => r?.typeId === "minecraft:player" || r?.id === p.id);
          if (!hasPlayerRider) continue;

          // Stop navigation (follow owner / wander / etc.)
          try { e.getComponent?.("minecraft:navigation")?.stop?.(); } catch {}

          // If the mob is currently chasing/attacking something, clearing target reduces steering conflicts.
          try { e.getComponent?.("minecraft:combat")?.setTarget?.(undefined); } catch {}

          // Sync mount rotation to rider view to prevent head/AI look from "fighting" the player's camera.
          // This keeps the mount facing where the rider is looking without locking the rider's own rotation.
          try {
            const riderPlayer = riders.find(r => r?.typeId === "minecraft:player") || riders.find(r => r?.id === p.id);
            if (riderPlayer && typeof riderPlayer.getRotation === "function" && typeof e.setRotation === "function") {
              const rot = riderPlayer.getRotation();
              // Clamp pitch to 0 to avoid weird nose-dives; yaw follows player camera.
              e.setRotation({ x: 0, y: rot?.y ?? 0 });
            }
          } catch {}

          // Mark it so other systems can opt-out while ridden
          try { e.addTag("pr_mount_ai_lock"); } catch {}
        } catch {}
      }
    }
  } catch {}
}, 2);
// --- End Mount AI Lock ---
// --- Battle Mode Selection (CHAT-based: avoids UI freezes) ---
// When you click your OWN pokemon with empty hand, you will be prompted in chat to pick PVE or PVP.
// Commands:
//  !pve -> start wild targeting (original behavior)
//  !pvp -> enter PvP targeting (click another player's pokemon to challenge)
//  !cancelbattle -> cancel pending selection / searches
//  !accept / !decline -> respond to PvP challenges
const __pendingBattleChoice = new Map(); // playerId -> pokemonEntityId
const __pvpTargeting = new Map(); // playerId -> attackerPokemonEntity
const __pendingChallenge = new Map(); // defenderPlayerId -> { fromName, fromId, attackerPokemonId, defenderPokemonId, expireTick }

// --- PvP turn-based tags (no dynamic-property registration needed) ---
// We use tags to avoid crashing the pack if entity dynamic properties aren't registered.
// Player tags:
//   pr:pvp_battle:<id>   -> player is in a pvp battle
//   pr:pvp_turn:<id>     -> it's this player's turn
// Pokemon tags:
//   pr:pvp_battle:<id>   -> pokemon is in that battle
function __newBattleId() {
  return String(system.currentTick) + "_" + Math.floor(Math.random() * 100000);
}
function __tagBattle(id) { return `pr:pvp_battle:${id}`; }
function __tagTurn(id) { return `pr:pvp_turn:${id}`; }
function __getBattleIdFromTags(ent) {
  try {
    for (const t of ent.getTags()) {
      if (t.startsWith("pr:pvp_battle:")) return t.substring("pr:pvp_battle:".length);
    }
  } catch {}
  return "";
}
function __clearPvpTags(ent) {
  try {
    for (const t of ent.getTags()) {
      if (t.startsWith("pr:pvp_battle:") || t.startsWith("pr:pvp_turn:")) ent.removeTag(t);
    }
  } catch {}
}


function safePlaySound(target, sound, volume = 1, pitch = 1) {
  try {
    // In some "restricted execution" contexts, native playSound() is blocked, but commands work.
    if (target && typeof target.runCommandAsync === "function") {
      target.runCommandAsync(`playsound ${sound} @s ~ ~ ~ ${volume} ${pitch}`);
      return;
    }
  } catch (e) {}
  try {
    if (target && typeof target.playSound === "function") target.playSound(sound);
  } catch (e) {}
}




function __entityValid(ent) {
    try {
        if (!ent) return false;
        // Some Script API builds expose isValid() while others expose isValid as a boolean or not at all.
        if (typeof ent.isValid === "function") return ent.isValid();
        if (typeof ent.isValid === "boolean") return ent.isValid;
        // Fallback: accessing id will throw if invalid on some builds
        void ent.id;
        return true;
    } catch (e) {
        return false;
    }
}

world.afterEvents.playerSpawn.subscribe(({ player }) => {
    if (
        !player.hasTag("pr:joined")
    ) {
        player.setDynamicProperty("pr:slot_1_name", undefined)
        player.setDynamicProperty("pr:slot_2_name", undefined)
        player.setDynamicProperty("pr:slot_3_name", undefined)
        player.setDynamicProperty("pr:slot_4_name", undefined)
        player.setDynamicProperty("pr:slot_5_name", undefined)
        player.setDynamicProperty("pr:slot_6_name", undefined)

        player.setDynamicProperty("pr:slot_1_level", 0)
        player.setDynamicProperty("pr:slot_2_level", 0)
        player.setDynamicProperty("pr:slot_3_level", 0)
        player.setDynamicProperty("pr:slot_4_level", 0)
        player.setDynamicProperty("pr:slot_5_level", 0)
        player.setDynamicProperty("pr:slot_6_level", 0)

        player.setDynamicProperty("pr:slot_1_health", 0)
        player.setDynamicProperty("pr:slot_2_health", 0)
        player.setDynamicProperty("pr:slot_3_health", 0)
        player.setDynamicProperty("pr:slot_4_health", 0)
        player.setDynamicProperty("pr:slot_5_health", 0)
        player.setDynamicProperty("pr:slot_6_health", 0)
      //  player.runCommand("give @s pr:arceus_phone")
        //player.runCommand("give @s pr:potion")
       // player.runCommand("give @s pr:pokeball 10")
        player.addTag("pr:joined")
    }
});


// --- Chat command handlers for battle mode + PvP challenge ---
// NOTE: This avoids opening UI forms during interact events (which can freeze some clients).
world.beforeEvents.chatSend.subscribe((ev) => {
    const player = ev.sender;
    // NOTE: Avoid using `instanceof Player` here. In some Bedrock beta builds
    // the sender object may not satisfy instanceof checks even though it's a player,
    // which would cause our chat commands to be ignored.
    if (!player || player.typeId !== "minecraft:player") return;

    const msgRaw = (ev.message ?? "").trim();
    if (!msgRaw.startsWith("!")) return;

    const msg = msgRaw.toLowerCase();
    const pid = player.id;

    // Helpers
    const clearPendingChoice = () => {
        __pendingBattleChoice.delete(pid);
    };

    // Cancel
    if (msg === "!cancelbattle") {
        ev.cancel = true;

        // cancel pve searching
        if (player.getDynamicProperty("pr:looking_for_battle") === true) {
            player.setDynamicProperty("pr:looking_for_battle", false);
        }

        // cancel pvp targeting
        __pvpTargeting.delete(pid);
        player.setDynamicProperty("pr:pvp_targeting", false);
        player.setDynamicProperty("pr:pvp_selected_pokemon", undefined);

        clearPendingChoice();
        safePlaySound(player, "random.orb");
        __safeActionBar(player, "§cBattle canceled.");
        return;
    }

    
    // Ride / Dismount (bulletproof: uses addon ownership properties, not vanilla tame)
    if (msg === "!dismount") {
        ev.cancel = true;
        system.run(async () => {
            // Prefer script API dismount (works without command permissions)
            try {
                const riding = player.getComponent?.("minecraft:riding");
                // If we can access the mount entity, remove flight group before dismounting
                try {
                    const mount = riding?.entityRidingOn ?? riding?.getEntityRidingOn?.();
                    if (mount) mount.triggerEvent?.("pr:disable_fly_mount");
                } catch {}
                if (riding?.stopRiding) riding.stopRiding();
            } catch {}
            // Fallback to /ride for older versions
            try { await player.runCommandAsync("ride @s stop_riding"); } catch {}
            safePlaySound(player, "random.orb");
            __safeActionBar(player, "§7Dismounted.");
        
            // cleanup pr_mount_ai_lock tags nearby so other AI systems resume normally
            try {
                let nearby = [];
                try { nearby = player.dimension.getEntities({ location: player.location, maxDistance: 64 }); }
                catch { try { nearby = player.dimension.getEntities(); } catch { nearby = []; } }

                for (const e of nearby) {
                    try {
                        if (!e?.typeId || typeof e.typeId !== "string" || !e.typeId.startsWith("pr:")) continue;
                        if (!e.hasTag?.("pr_mount_ai_lock")) continue;

                        const rideable = e.getComponent?.("minecraft:rideable");
                        const riders = (rideable?.getRiders?.() ?? rideable?.riders ?? []);
                        if (riders && riders.length > 0) continue;

                        e.removeTag?.("pr_mount_ai_lock");
                    } catch {}
                }
            } catch {}
});
        return;
    }

    if (msg === "!ride") {
        ev.cancel = true;

        // Prefer the last pokemon you clicked (same one used for !pve/!pvp prompt)
        let target = __pendingBattleChoice.get(pid);
        if (target && !__entityValid(target)) target = undefined;

        // If no pending target, find the nearest owned pokemon within 24 blocks
        if (!target) {
            let candidates = [];
            try {
                candidates = player.dimension.getEntities({ location: player.location, maxDistance: 24 });
            } catch {
                try { candidates = player.dimension.getEntities(); } catch { candidates = []; }
            }

            // Filter to owned pokemon entities
            const isMine = (e) => {
                try {
                    const typeOk = typeof e.typeId === "string" && e.typeId.startsWith("pr:");
                    if (!typeOk) return false;
                    const ownerId = e.getDynamicProperty?.("pr:owner");
                    const ownerName = e.getDynamicProperty?.("pr:owner_name");
                    return (ownerId && ownerId === player.id) || (ownerName && (ownerName === player.name || ownerName === player.nameTag));
                } catch { return false; }
            };

            // pick closest by distance squared
            let best = undefined;
            let bestD2 = Infinity;
            for (const e of candidates) {
                if (!e || !e.location || !isMine(e)) continue;
                const dx = e.location.x - player.location.x;
                const dy = e.location.y - player.location.y;
                const dz = e.location.z - player.location.z;
                const d2 = dx*dx + dy*dy + dz*dz;
                if (d2 < bestD2) { bestD2 = d2; best = e; }
            }
            target = best;
        }

        if (!target) {
            __safeActionBar(player, "§cCouldn't start riding. Make sure your pokemon is nearby (within 24 blocks).");
            return;
        }

                // Ensure the rideable "tamed" component group is applied on the entity
        try { target.triggerEvent("tamed"); } catch (e) {}

// Use a temporary tag so the /ride selector can target the exact entity
        const safeId = String(pid || "").replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "p";
        const rideTag = `pr_ride_${safeId}`;
        try { target.addTag(rideTag); } catch {}

        system.run(async () => {
            try {
                // Prefer script API mounting (works without command permissions)
                let mounted = false;
                try {
                    const rideable = target.getComponent?.("minecraft:rideable");
                    if (rideable?.addRider) {
                        rideable.addRider(player);
                        mounted = true;
                    }
                } catch {}

                // Fallback to /ride for older versions
                if (!mounted) {
                    await player.runCommandAsync(`ride @s start_riding @e[tag=${rideTag},c=1]`);
                }

                // Enable flight controls for flyable pokemon while mounted (Pokédex + override list)
try {
    const isFlyable = __isFlyablePokemon(target?.typeId);
    if (isFlyable) target.triggerEvent("pr:enable_fly_mount");
    else target.triggerEvent("pr:disable_fly_mount");
} catch {}

                safePlaySound(player, "random.orb");
                __safeActionBar(player, "§aRiding!");
            } catch (e) {
                const em = String(e?.message || e || "");
                if (em.toLowerCase().includes("permission")) {
                    __safeActionBar(player, "§cRide failed: no permission to use /ride. Enable cheats / OP the player, or use a runtime that supports script mounting.");
                } else {
                    __safeActionBar(player, "§cCouldn\'t start riding. (That pokemon may not be rideable yet.)");
                }
            } finally {
                try { target.removeTag(rideTag); } catch {}
            }
        });
        return;
    }

// Choose PVE / PVP when prompted
    if (msg === "!pve" || msg === "!pvp") {
        // Get the pokemon entity that was clicked when the prompt was shown.
        const pokemon = __pendingBattleChoice.get(pid);
        if (!pokemon) return; // not prompted / not for us
        ev.cancel = true;

        clearPendingChoice();

        if (!__entityValid(pokemon)) {
            __safeActionBar(player, "§cThat pokemon is no longer available.");
            return;
        }

        if (msg === "!pve") {
            // Original behavior: mark for battle search, then click wild pokemon
            safePlaySound(player, "random.orb");
            pokemon.setDynamicProperty("pr:looking_for_battle", true);
            player.setDynamicProperty("pr:looking_for_battle", true);
            player.setDynamicProperty("pr:battle_mode", "pve");
            __safeActionBar(player, "§aPVE: Click a wild pokemon to battle.");
            return;
        }

        // PVP
        safePlaySound(player, "random.orb");
        __pvpTargeting.set(pid, pokemon); // store entity ref (no Dimension.getEntity needed)
        player.setDynamicProperty("pr:pvp_targeting", true);
        player.setDynamicProperty("pr:battle_mode", "pvp");
        __safeActionBar(player, "§ePVP: Click another player's pokemon to challenge them.");
        return;
    }

    // Re-open PvP battle menu (only on your turn) without spamming UI
    if (msg === "!battle") {
        const battleId = __getBattleIdFromTags(player);
        if (!battleId) return;
        ev.cancel = true;

        const turnTag = __tagTurn(battleId);
        if (!player.hasTag(turnTag)) {
            __safeActionBar(player, "§7It's not your turn.");
            return;
        }

        const battleTag = __tagBattle(battleId);
        let pokes = [];
        try {
            pokes = player.dimension.getEntities({ tags: [battleTag] });
        } catch {
            try {
                pokes = player.dimension.getEntities().filter(e => e.getTags && e.getTags().includes(battleTag));
            } catch { pokes = []; }
        }

        const myPoke = pokes.find(e => (e.getDynamicProperty && e.getDynamicProperty("pr:owner") === player.id) || (e.getDynamicProperty && e.getDynamicProperty("pr:owner_name") === player.nameTag));
        const oppPoke = pokes.find(e => e !== myPoke);
        if (!myPoke || !oppPoke) {
            __safeActionBar(player, "§cCouldn't find battling pokemon. Move closer and try again.");
            return;
        }

        system.run(() => {
            try { battleMenu(player, myPoke, oppPoke, 0); } catch {}
        });
        return;
    }


// Accept / Decline PvP challenge
    if (msg === "!accept" || msg === "!decline") {
        const pending = __pendingChallenge.get(pid);
        if (!pending) return;
        ev.cancel = true;

        const nowTick = system.currentTick;
        if (pending.expireTick && nowTick > pending.expireTick) {
            __pendingChallenge.delete(pid);
            __safeActionBar(player, "§cChallenge expired.");
            return;
        }

        // Resolve challenger
        const challenger = world.getAllPlayers().find(p => p.id === pending.fromId) || world.getAllPlayers().find(p => p.nameTag === pending.fromName);
        if (!challenger) {
            __pendingChallenge.delete(pid);
            __safeActionBar(player, "§cChallenger is no longer online.");
            return;
        }

        if (msg === "!decline") {
            __pendingChallenge.delete(pid);
            __safeActionBar(challenger, `§c${player.nameTag} declined your challenge.`);
            __safeActionBar(player, "§cDeclined.");
            return;
        }

        // Accept: start PvP battle (turn-based using existing battleMenu UI)
        __pendingChallenge.delete(pid);

        const attackerPokemon = pending.attackerPokemon;
        const defenderPokemon = pending.defenderPokemon;

        if (!attackerPokemon || !defenderPokemon) {
            __safeActionBar(challenger, "§cChallenge failed (pokemon missing).");
            __safeActionBar(player, "§cChallenge failed (pokemon missing).");
            return;
        }

        // Lock in-battle flags like your wild flow does
        attackerPokemon.setDynamicProperty("pr:on_battle", true);
        defenderPokemon.setDynamicProperty("pr:on_battle", true);

        challenger.setDynamicProperty("pr:looking_for_battle", false);
        attackerPokemon.setDynamicProperty("pr:looking_for_battle", false);

        // Create a battle id and tag both players + both pokemons.
        const battleId = __newBattleId();

        // NOTE: addTag/removeTag are restricted in chat-event execution; defer to next tick.
        system.run(() => {
            try {
                
        __clearPvpTags(challenger);
        __clearPvpTags(player);
        __clearPvpTags(attackerPokemon);
        __clearPvpTags(defenderPokemon);

        challenger.addTag(__tagBattle(battleId));
        player.addTag(__tagBattle(battleId));
        attackerPokemon.addTag(__tagBattle(battleId));
        defenderPokemon.addTag(__tagBattle(battleId));

        // Challenger goes first
        challenger.addTag(__tagTurn(battleId));

        safePlaySound(challenger, "random.orb");
        safePlaySound(player, "random.orb");
        __safeActionBar(challenger, `§a${player.nameTag} accepted! §eYour turn.`);
        __safeActionBar(player, "§aBattle started! §7Waiting for opponent...");

                // Open the existing battle UI for the challenger (their pokemon is p1)
                try { battleMenu(challenger, attackerPokemon, defenderPokemon, 0); } catch {}
            } catch (e) {
                try { __safeActionBar(challenger, "§cPvP failed to start (tag restriction)."); } catch {}
                try { __safeActionBar(player, "§cPvP failed to start (tag restriction)."); } catch {}
            }
        });
        return;
    }
});


// Ja foi
world.afterEvents.playerInteractWithBlock.subscribe((event) => {
    const { block, player, itemStack } = event
    if (block.typeId.includes("apricorn")) {
        if (block.permutation.getState("pr:has_apricorn") == true) {
            const randomNumber = Math.floor(Math.random() * 4) + 1;
            if (block.typeId.includes("blue")) {
                const item = new ItemStack("pr:blueapricorn", randomNumber);
                block.dimension.spawnItem(item, block.location)
            } else if (block.typeId.includes("yellow")) {
                const item = new ItemStack("pr:yellowapricorn", randomNumber);
                block.dimension.spawnItem(item, block.location)
            } else if (block.typeId.includes("white")) {
                const item = new ItemStack("pr:whiteapricorn", randomNumber);
                block.dimension.spawnItem(item, block.location)
            } else if (block.typeId.includes("pink")) {
                const item = new ItemStack("pr:pinkapricorn", randomNumber);
                block.dimension.spawnItem(item, block.location)
            } else if (block.typeId.includes("black")) {
                const item = new ItemStack("pr:blackapricorn", randomNumber);
                block.dimension.spawnItem(item, block.location)
            } else if (block.typeId.includes("green")) {
                const item = new ItemStack("pr:greenapricorn", randomNumber);
                block.dimension.spawnItem(item, block.location)
            } else if (block.typeId.includes("red")) {
                const item = new ItemStack("pr:redapricorn", randomNumber);
                block.dimension.spawnItem(item, block.location)
            }

            block.setPermutation(
                block.permutation.withState("pr:has_apricorn", false)
            );
        }
    }
})
/*
world.beforeEvents.worldInitialize.subscribe((event) => {
    const { blockComponentRegistry } = event
    blockComponentRegistry.registerCustomComponent("pr:interact", {
        onPlayerInteract(event) {
        }
    })
    blockComponentRegistry.registerCustomComponent("pr:tick", {
        onTick(event) {
            const { block } = event
            block.setPermutation(
                block.permutation.withState("pr:has_apricorn", true)
            );
        }
    })
})*/
// Ja foi
const pokeballsCaptureRate = [
    { "pr:pokeball": 1.65 },
    { "pr:great_ball": 1.5 },
    { "pr:beast_ball": 1.0 },
    { "pr:ultra_ball": 2.0 },
    { "pr:master_ball": 255.0 },
    { "pr:safari_ball": 1.5 },
    { "pr:level_ball": 2.0 },
    { "pr:lure_ball": 3.0 },
    { "pr:moon_ball": 4.0 },
    { "pr:friend_ball": 1.0 },
    { "pr:love_ball": 8.0 },
    { "pr:heavy_ball": 4.0 },
    { "pr:net_ball": 3.0 },
    { "pr:timer_ball": 4.0 },
    { "pr:repeat_ball": 3.0 },
    { "pr:premier_ball": 1.0 },
    { "pr:nest_ball": 4.0 },
    { "pr:dive_ball": 3.5 },
    { "pr:luxury_ball": 1.0 },
    { "pr:heal_ball": 1.0 },
    { "pr:quick_ball": 5.0 },
    { "pr:dusk_ball": 3.5 },
    { "pr:cherish_ball": 1.0 },
    { "pr:dream_ball": 255.0 }
];
// Ja foi
function tryCatch(currentHP, maxHP, catchRate, ballBonus) {
    const chance = ((3 * maxHP - 2 * currentHP) * catchRate * ballBonus) / (3 * maxHP);
    const adjustedChance = chance * 3.0;
    const finalChance = Math.min(adjustedChance / 255, 1);
    const result = Math.random() < finalChance;
    return result;
}
// Ja foi
function getBallBonus(typeId) {
    const ball = pokeballsCaptureRate.find(entry => Object.keys(entry)[0] === typeId);
    return ball ? Object.values(ball)[0] : 1.0;
}
// Ja foi
world.afterEvents.itemUse.subscribe((data) => {
    let { source: player, itemStack: item } = data;
    if (item.typeId == "minecraft:stick") {
        if (player.isSneaking) {
            player.addTag("releaseSlot")
            safePlaySound(player, "random.orb");} else {
            player.addTag("changeSlot")
            safePlaySound(player, "random.click");}
    }
});
// Ja foi
export function optionsMenu(player) {
    let form = new ActionFormData();
    let functions = [];
    form.title("Options");
    form.body(`000000000000`)
    {
        form.button("Set");
        functions.push((res) => {
            optionsMenuRA(player)
        });
    }
    {
        form.button("Remove");
        functions.push((res) => {
            optionsMenuAR(player)
        });
    }
    {
        form.button("Close");
        functions.push((res) => {
        });
    }
    form.show(player).then((res) => {
        const canceled = res.canceled;
        if (canceled == true) {
            return;
        }
        functions[res.selection]();
    });
}
// Ja foi
export function optionsMenuRA(player) {
    let form = new ActionFormData();
    let functions = [];
    form.title("Options");
    form.body(`000000000000`)
    const inv = player.getComponent("minecraft:inventory").container
    for (let i = 1; i < 7; i++) {
        form.button(`Slot ` + i);
        functions.push((res) => {
            if (player.getDynamicProperty("pr:sended_pokemon_slot") != i) {
                if (player.getDynamicProperty(`pr:slot_${i}_name`) == undefined) {
                    selectPokeToSlot(player, i)
                } else {
                    __safeActionBar(player, "You already have a pokemon in this slot, remove to add another one!")
                }
            } else {
                __safeActionBar(player, "There is already a pokemon in this slot, your main one!")
            }
        });
    }
    {
        form.button("Close");
        functions.push((res) => {
        });
    }
    form.show(player).then((res) => {
        const canceled = res.canceled;
        if (canceled == true) {
            return;
        }
        functions[res.selection]();
    });
}
// Ja foi
export function optionsMenuAR(player) {
    let form = new ActionFormData();
    let functions = [];
    form.title("Options");
    form.body(`000000000000`)
    const inv = player.getComponent("minecraft:inventory").container
    for (let i = 1; i < 7; i++) {
        form.button(`Slot ` + i);
        functions.push((res) => {
            if (player.getDynamicProperty("pr:sended_pokemon_slot") != i) {
                if (player.getDynamicProperty(`pr:slot_${i}_name`) != undefined) {
                    const itemNew = new ItemStack("pr:" + player.getDynamicProperty(`pr:slot_${i}_name`) + "_spawn_egg", 1);
                    itemNew.setLore([
                        `${formatAttackName(player.getDynamicProperty(`pr:slot_${i}_name`))} Tamed`,
                        `Owner:§7 ${player.nameTag}`,
                        `Level:§7 ${player.getDynamicProperty(`pr:slot_${i}_level`)}`,
                        `Health:§7 ${player.getDynamicProperty(`pr:slot_${i}_health_true`)}`
                    ]);
                    player.dimension.spawnItem(itemNew, player.location)
                    player.setDynamicProperty(`pr:slot_${i}_level`, 0)
                    player.setDynamicProperty(`pr:slot_${i}_health`, 0)
                    player.setDynamicProperty(`pr:slot_${i}_health_true`, 0)
                    player.setDynamicProperty(`pr:slot_${i}_name`, undefined)
                } else {
                    __safeActionBar(player, "You don't have a pokemon in this slot!")
                }
            } else {
                __safeActionBar(player, "There is already a pokemon in this slot, your main one!")
            }
        });
    }
    {
        form.button("Close");
        functions.push((res) => {
        });
    }
    form.show(player).then((res) => {
        const canceled = res.canceled;
        if (canceled == true) {
            return;
        }
        functions[res.selection]();
    });
}
// Ja foi
export function selectPokeToSlot(player, slot) {
    let form = new ActionFormData();
    let functions = [];
    form.title("Options");
    form.body(`000000000000`)
    const inv = player.getComponent("minecraft:inventory").container
    for (let i = 0; i < inv.size; i++) {
        const item = inv.getItem(i);
        if (item) {
            if (item.getLore().length > 0 && item.getLore()[0].includes("Tamed") && !item.getLore()[3].includes("Death")) {
                const pokemonData = pokemonsData.find(pokemon => pokemon.id == item.typeId.replace("_spawn_egg", ""))
                let pokeHealth = item.getLore()[3].replace("Health:§7 ", "")
                let pokeLevel = item.getLore()[2].replace("Level:§7 ", "")
                let pokeMaxHealth = pokemonData.maxHealth
                let pokeHBar = Math.round((pokeHealth / pokeMaxHealth) * 100);
                form.button("§f" + formatItemName(item) + "§7\nLvl.§r" + pokeLevel + " §0|§7 HP: §r" + pokeHealth, "textures/icons/pokemons/" + formatItemName2(item));
                functions.push((res) => {
                    player.setDynamicProperty(`pr:slot_${slot}_name`, formatItemName2(item))
                    player.setDynamicProperty(`pr:slot_${slot}_level`, pokeLevel)
                    player.setDynamicProperty(`pr:slot_${slot}_health`, pokeHBar)
                    player.setDynamicProperty(`pr:slot_${slot}_health_true`, pokeHealth)
                    console.warn(pokeHBar + " | " + pokeLevel)
                    inv.setItem(i)
                });
            }
        }
    }
    {
        form.button("Close");
        functions.push((res) => {
        });
    }
    form.show(player).then((res) => {
        const canceled = res.canceled;
        if (canceled == true) {
            return;
        }
        functions[res.selection]();
    });
}
// Ja foi
function formatItemName(item) {
    if (!item || !item.typeId) return "Unknown";

    let id = item.typeId;

    id = id.replace("_spawn_egg", "");

    if (id.includes(":")) {
        id = id.split(":")[1];
    }

    // Troca _ por espaço
    id = id.replace(/_/g, " ");

    // Deixa a primeira letra de cada palavra maiúscula
    id = id.split(" ").map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");

    return id;
}
// Ja foi
function formatItemName2(item) {
    if (!item || !item.typeId) return "Unknown";

    let id = item.typeId;

    id = id.replace("_spawn_egg", "");
    id = id.replace("_fainted", "");

    if (id.includes(":")) {
        id = id.split(":")[1];
    }

    // Troca _ por espaço
    id = id.replace(/_/g, " ");

    // Deixa a primeira letra de cada palavra maiúscula
    id = id.split(" ").map(word => word.charAt(0).toLowerCase() + word.slice(1)).join(" ");

    return id;
}
// Ja foi
export function formatName(name) {
    if (!name) return "Unknown";

    let id = name;

    id = id.replace("_spawn_egg", "");

    if (id.includes(":")) {
        id = id.split(":")[1];
    }

    // Troca _ por espaço
    id = id.replace(/_/g, " ");

    id = id.split(" ").map(word => word.charAt(0).toLowerCase() + word.slice(1)).join(" ");

    id = id.replace(" ", "");

    return id;
}

const TIME_20_MIN = 1200; // 20 minutos em segundos reais

function evoSwapPokemon(player, pokemon, evoTargetId, newLevel) {
    try {
        // FX on the Pokémon
        try { pokemon.dimension.spawnParticle("snowstorm:magic", pokemon.location);
        try { pokemon.dimension.spawnParticle("pr:evolution_burst", pokemon.location); } catch (e) {} } catch (e) {}
        try { safePlaySound(player, "random.orb"); } catch (e) {}

        const slot = pokemon.getDynamicProperty("pr:slot");
        const isSended = pokemon.getDynamicProperty("pr:sended_pokemon") === true;

        // Capture transferable properties
        const nick = pokemon.getDynamicProperty("pr:nickname");
        const owner = pokemon.getDynamicProperty("pr:owner");
        const ownerName = pokemon.getDynamicProperty("pr:owner_name");
        const tamed = pokemon.getDynamicProperty("pr:tamed");
        const move1 = pokemon.getDynamicProperty("pr:move1");
        const move2 = pokemon.getDynamicProperty("pr:move2");
        const move3 = pokemon.getDynamicProperty("pr:move3");
        const move4 = pokemon.getDynamicProperty("pr:move4");
        const onBattle = pokemon.getDynamicProperty("pr:on_battle");

        // Fast evolve (1.5s)
        system.runTimeout(() => {
            try {
                const loc = pokemon.location;
                const dim = pokemon.dimension;
                const evolved = dim.spawnEntity(evoTargetId, {x: loc.x, y: loc.y, z: loc.z});
                try { dim.spawnParticle("pr:evolution_burst", {x: loc.x, y: loc.y + 1, z: loc.z}); } catch (e) {}

                evolved.setDynamicProperty("pr:level", newLevel);
                evolved.setDynamicProperty("pr:nickname", nick);
                evolved.setDynamicProperty("pr:owner", owner);
                evolved.setDynamicProperty("pr:owner_name", ownerName);
                evolved.setDynamicProperty("pr:tamed", tamed);
                evolved.setDynamicProperty("pr:move1", move1);
                evolved.setDynamicProperty("pr:move2", move2);
                evolved.setDynamicProperty("pr:move3", move3);
                evolved.setDynamicProperty("pr:move4", move4);
                evolved.setDynamicProperty("pr:on_battle", onBattle);

                // Preserve slot linkage so Return stores correctly
                evolved.setDynamicProperty("pr:slot", slot);
                evolved.setDynamicProperty("pr:sended_pokemon", isSended);

                try { evolved.getComponent("minecraft:tameable")?.tame(player); } catch (e) {}

                // Full heal on evolution
                const evoInfo = pokemonsData.find(p => p.id === evoTargetId);
                const hp = evolved.getComponent("minecraft:health");
                if (evoInfo && hp) hp.setCurrentValue(evoInfo.maxHealth);

                // Update name tag
                if (nick == undefined) {
                    evolved.nameTag = formatEntityName(evolved) + " | Lvl: §a" + evolved.getDynamicProperty("pr:level") + "§r\n" + "Owner:§a " + ownerName;
                } else {
                    evolved.nameTag = nick + " | Lvl: §a" + evolved.getDynamicProperty("pr:level") + "§r\n" + "Owner:§a " + ownerName;
                }

                // Update player active pokemon id if needed
                if (isSended) {
                    player.setDynamicProperty("pr:sended_pokemon_id", evolved.id);
                }

                // Remove old
                pokemon.remove();

                // Announce
                player.sendMessage(`§a${formatEntityName2({typeId: pokemon.typeId})} evolved into ${formatEntityName2({typeId: evolved.typeId})}!`);
            } catch (e) {}
        }, 30);
    } catch (e) {}
}

system.runInterval(() => {
    for (let player of world.getAllPlayers()) {
        let timer = player.getDynamicProperty("pr:timerInGame") ?? 0;
        timer++;
        player.setDynamicProperty("pr:timerInGame", timer);

        if (timer % TIME_20_MIN === 0) {
            const minutes = (timer / TIME_20_MIN) * 20;
            safePlaySound(player, "random.levelup");
            player.runCommand(`tellraw @s {"rawtext":[{"text":"§l§6P§eO§aK§bÉ§dM§cO§6N§r §7» §aNice! §eYou\u0027ve played §b${minutes} minutes§r§e. Claim your §aDaily Reward§r§e! §6✨§r"}]}`);
        }
    }
}, 20); 

// Ja foi
system.runInterval(() => {
    const players = world.getAllPlayers();
    for (let player of players) {
        const slot1_name = player.getDynamicProperty("pr:slot_1_name")
        const slot2_name = player.getDynamicProperty("pr:slot_2_name")
        const slot3_name = player.getDynamicProperty("pr:slot_3_name")
        const slot4_name = player.getDynamicProperty("pr:slot_4_name")
        const slot5_name = player.getDynamicProperty("pr:slot_5_name")
        const slot6_name = player.getDynamicProperty("pr:slot_6_name")

        const slot1_level = player.getDynamicProperty("pr:slot_1_level")
        const slot2_level = player.getDynamicProperty("pr:slot_2_level")
        const slot3_level = player.getDynamicProperty("pr:slot_3_level")
        const slot4_level = player.getDynamicProperty("pr:slot_4_level")
        const slot5_level = player.getDynamicProperty("pr:slot_5_level")
        const slot6_level = player.getDynamicProperty("pr:slot_6_level")


// === Learn Points: award on battle (and any non-candy) level-ups ===
// The battle level system updates pr:slot_<n>_level; rare candy already awards points separately.
// We watch for level increases here and award +1 Learn Point per level gained.
try {
    const levels = [slot1_level, slot2_level, slot3_level, slot4_level, slot5_level, slot6_level];
    for (let s = 1; s <= 6; s++) {
        const cur = Number(levels[s - 1]);
        if (!Number.isFinite(cur) || cur <= 0) continue;

        const lastKey = `pr:lp_last_level_s${s}`;
        const last = Number(player.getDynamicProperty(lastKey));

        if (!Number.isFinite(last) || last <= 0) {
            player.setDynamicProperty(lastKey, cur);
            continue;
        }

        if (cur > last) {
            // Skip if candy already awarded this tick (or a few ticks ago)
            const skipTick = Number(player.getDynamicProperty(`pr:lp_skip_s${s}`));
            const recentlySkipped = Number.isFinite(skipTick) && (system.currentTick - skipTick) <= 5;

            player.setDynamicProperty(lastKey, cur);

            if (recentlySkipped) continue;

            const gained = cur - last;
            addLearnPoints(player, s, gained).then((total) => {
                try { cacheLearnPoints(player, s, total); } catch (e) { }
                try {
                    if (gained === 1) player.sendMessage(`§a+1 Learn Point§r (Total: §e${total}§r)`);
                    else player.sendMessage(`§a+${gained} Learn Points§r (Total: §e${total}§r)`);
                } catch (e) { }
            }).catch(() => {});
        }
    }
} catch (e) { }


        const slot1_health = player.getDynamicProperty("pr:slot_1_health")
        const slot2_health = player.getDynamicProperty("pr:slot_2_health")
        const slot3_health = player.getDynamicProperty("pr:slot_3_health")
        const slot4_health = player.getDynamicProperty("pr:slot_4_health")
        const slot5_health = player.getDynamicProperty("pr:slot_5_health")
        const slot6_health = player.getDynamicProperty("pr:slot_6_health")
        let selectedSlot = player.getDynamicProperty("pr:selectedSlot")
        if (selectedSlot == undefined) {
            player.setDynamicProperty("pr:selectedSlot", 1)
        }
        const maxLenghtName = 16
        const maxLenghtLevel = 5
        const maxLenghtHealth = 5
        if (player.hasTag("changeSlot")) {
            player.removeTag("changeSlot")
            if (selectedSlot < 6) {
                player.setDynamicProperty("pr:selectedSlot", selectedSlot + 1)
            } else {
                player.setDynamicProperty("pr:selectedSlot", 1)
            }
        }

        if (player.hasTag("pr:debug")) {
            player.removeTag("pr:debug")
            player.setDynamicProperty("pr:sended_pokemon", false)
            player.setDynamicProperty("pr:sended_pokemon_id", 0)
            player.setDynamicProperty("pr:sended_pokemon_slot", 0)
        }
        if (player.hasTag("releaseSlot")) {
            player.removeTag("releaseSlot")
            if (selectedSlot == 1) {
                console.warn(slot1_name + " | " + slot1_level + " | " + slot1_health + "%")
            } else if (selectedSlot == 2) {
                console.warn(slot2_name + " | " + slot2_level + " | " + slot2_health + "%")
            } else if (selectedSlot == 3) {
                console.warn(slot3_name + " | " + slot3_level + " | " + slot3_health + "%")
            } else if (selectedSlot == 4) {
                console.warn(slot4_name + " | " + slot4_level + " | " + slot4_health + "%")
            } else if (selectedSlot == 5) {
                console.warn(slot5_name + " | " + slot5_level + " | " + slot5_health + "%")
            } else if (selectedSlot == 6) {
                console.warn(slot6_name + " | " + slot6_level + " | " + slot6_health + "%")
            }
        }
        /*player.onScreenDisplay.setTitle(
            `${slot1_name}`.padEnd(maxLenghtName, " ") + `${slot1_level}`.padEnd(maxLenghtLevel, " ") + `${slot1_health}`.padEnd(maxLenghtHealth, " ") +
            `${slot2_name}`.padEnd(maxLenghtName, " ") + `${slot2_level}`.padEnd(maxLenghtLevel, " ") + `${slot2_health}`.padEnd(maxLenghtHealth, " ") +
            `${slot3_name}`.padEnd(maxLenghtName, " ") + `${slot3_level}`.padEnd(maxLenghtLevel, " ") + `${slot3_health}`.padEnd(maxLenghtHealth, " ") +
            `${slot4_name}`.padEnd(maxLenghtName, " ") + `${slot4_level}`.padEnd(maxLenghtLevel, " ") + `${slot4_health}`.padEnd(maxLenghtHealth, " ") +
            `${slot5_name}`.padEnd(maxLenghtName, " ") + `${slot5_level}`.padEnd(maxLenghtLevel, " ") + `${slot5_health}`.padEnd(maxLenghtHealth, " ") +
            `${slot6_name}`.padEnd(maxLenghtName, " ") + `${slot6_level}`.padEnd(maxLenghtLevel, " ") + `${slot6_health}`.padEnd(maxLenghtHealth, " ") +
            `S:${selectedSlot}`.padEnd(5, " ")
        )*/
        const pokemons = player.dimension.getEntities({
            location: player.location,
            families: ["pokemon"],
            maxDistance: 32,
        })
        if (!player.hasTag("pr:started")) {
            const moving = (player.getVelocity().x != 0 || player.getVelocity().z != 0);
            if (!moving && !player.hasTag("pr:intro_complete") && !player.hasTag("pr:starter_menu_open") && !player.hasTag("pr:starter_menu_pending")) {
                __safeActionBar(player, "Move to begin your journey!");
            } else if (moving && !player.hasTag("pr:intro_complete") && !player.hasTag("pr:starter_menu_open") && !player.hasTag("pr:starter_menu_pending")) {
                try { player.addTag("pr:starter_menu_pending"); } catch (e) {}
                system.runTimeout(() => {
                    try {
                        if (!player.hasTag("pr:intro_complete") && !world.getDynamicProperty(`pr:starter_${player.nameTag}`) && !player.hasTag("pr:starter_menu_open")) {
                            try {
                                if (!player.hasTag("pr:juniper_voice_played")) {
                                    player.playSound("pr:juniper_intro");
                                    player.addTag("pr:juniper_voice_played");
                                }
                            } catch (e) {}
                            try { player.addTag("pr:intro_complete"); } catch (e) {}
                            starterMenu(player, 0);
                        }
                    } catch (e) {}
                    try { player.removeTag("pr:starter_menu_pending"); } catch (e) {}
                }, 2);
            } else if (player.hasTag("pr:intro_complete") && !isStarter && !player.hasTag("pr:starter_menu_open") && !player.hasTag("pr:starter_menu_pending")) {
                try { player.addTag("pr:starter_menu_pending"); } catch (e) {}
                system.runTimeout(() => {
                    try {
                        if (player.hasTag("pr:intro_complete") && !world.getDynamicProperty(`pr:starter_${player.nameTag}`) && !player.hasTag("pr:starter_menu_open")) {
                            starterMenu(player, 0);
                        }
                    } catch (e) {}
                    try { player.removeTag("pr:starter_menu_pending"); } catch (e) {}
                }, 2);
            }
        }
        if (player.getDynamicProperty("pr:looking_for_battle") == true) {
            __safeActionBar(player, "Interact with another pokemon to start a battle!\n§7Interact again with our pokemon to cancel the search.")
        } else if (player.hasTag("pr:first_village_quest") && !player.hasTag("pr:first_village_quest_done")) {
            __safeActionBar(player, "§6Quest: §eFind a village and speak to a quest NPC.")
        }
        for (let pokemon of pokemons) {
            const pokemonData = pokemonsData.find(pokemonN => pokemonN.id == pokemon.typeId)
            if (pokemonData) {
                const Health = pokemon.getComponent("minecraft:health");
                if (Health.currentValue > pokemonData.maxHealth) {
                    Health.setCurrentValue(pokemonData.maxHealth)
                }
                if (pokemon?.getComponent("scale")?.value != 0) {
                    pokemon?.setDynamicProperty("pr:capturing", false)
                }
                if (pokemon.getDynamicProperty("pr:on_battle") === true) {
                    pokemon.addEffect("slowness", 2, {
                        amplifier: 255,
                        showParticles: false
                    });
                }
            }
        }
    }
}, 1);
// Ja foi
world.afterEvents.projectileHitEntity.subscribe((event) => {
    const { dimension, source, projectile } = event
    const entity = event.getEntityHit().entity
    const projectileId = projectile.typeId;

    if (projectileId.startsWith("pr:") && projectileId.includes("ball")) {
        const pokemonData = pokemonsData.find(pokemon => pokemon.id == entity.typeId)
        if (entity?.getComponent("scale")?.value != 0 && entity.getDynamicProperty("pr:capturing") != true && entity.getDynamicProperty("pr:tamed") != true) {
            if (pokemonData) {
                //safePlaySound(source, "pr:pokeball");const currentHealthValue = entity.getComponent("minecraft:health").currentValue;
                const maxHealthValue = entity.getComponent("minecraft:health").effectiveMax;

                const catchRate = pokemonData.catchRate ?? 45;

                const ballBonus = getBallBonus(projectileId);
                const captured = tryCatch(currentHealthValue, maxHealthValue, catchRate, ballBonus);
                catchingDisplay(source, captured, entity, projectile)
                entity.triggerEvent("onPoke");
                projectile.triggerEvent("remove");
                projectile.clearVelocity();
                projectile.playAnimation("catch");
                safePlaySound(source, "pr:pokeball");entity.addEffect("invisibility", 6 * 20, {
                    amplifier: 1,
                    showParticles: false
                });
                entity.addEffect("slowness", 6 * 20, {
                    amplifier: 255,
                    showParticles: false
                });
                entity.setDynamicProperty("pr:capturing", true)
                system.runTimeout(() => {
                    if (captured) {
                        const item = new ItemStack(entity.typeId + "_spawn_egg", 1);
                        item.setLore([
                            `${formatEntityName(entity)} Tamed`,
                            `Owner:§7 ${source.nameTag}`,
                            `Level:§7 ${entity.getDynamicProperty("pr:level")}`,
                            `Health:§7 ${currentHealthValue}`
                        ]);
                        entity.dimension.spawnItem(item, entity.location);
                        entity.remove();
                    } else {
                        entity.setDynamicProperty("pr:capturing", false)
                        const itemBall = new ItemStack(projectile.typeId, 1);
                        entity.dimension.spawnItem(itemBall, entity.location);
                    }
                }, 6 * 20);
            }
        }
    }
})
// Ja foi
function catchingDisplay(source, captured, pokemon, pokeball) {
    if (!source.hasTag("pr:capturing")) {
        source.camera.setCamera("minecraft:free", {
            location: {
                x: pokemon?.location.x - (source.getViewDirection().x * 2),
                y: pokemon.location.y + 1.5,
                z: pokemon?.location.z - (source.getViewDirection().z * 2),
            },
            rotation: pokemon.getRotation(),
            easeOptions: {
                easeTime: 0.25,
                easeType: EasingType.Linear,
            },
            facingEntity: pokeball,

        });
        source.addTag("pr:capturing")
        source.runCommand("inputpermission set @s camera disabled")
        source.runCommand("inputpermission set @s movement disabled")
        system.runTimeout(() => {
            __safeActionBar(source, "Capturing")
            safePlaySound(source, "random.orb");system.runTimeout(() => {
                __safeActionBar(source, "Capturing.")
                safePlaySound(source, "random.orb");system.runTimeout(() => {
                    __safeActionBar(source, "Capturing..")
                    safePlaySound(source, "random.orb");system.runTimeout(() => {
                        __safeActionBar(source, "Capturing...")
                        safePlaySound(source, "random.orb");system.runTimeout(() => {
                            __safeActionBar(source, "Capturing")
                            safePlaySound(source, "random.orb");system.runTimeout(() => {
                                __safeActionBar(source, "Capturing.")
                                safePlaySound(source, "random.orb");system.runTimeout(() => {
                                    __safeActionBar(source, "Capturing..")
                                    safePlaySound(source, "random.orb");system.runTimeout(() => {
                                        __safeActionBar(source, "Capturing...")
                                        safePlaySound(source, "random.orb");system.runTimeout(() => {
                                            if (captured == true) {
                                                __safeActionBar(source, "§a" + formatEntityName(pokemon) + "§r Captured!")
                                                safePlaySound(source, "random.levelup");} else {
                                                __safeActionBar(source, formatEntityName(pokemon) + " escaped!")
                                                safePlaySound(source, "random.pop2");}
                                            source.runCommand("inputpermission set @s camera enabled")
                                            source.runCommand("inputpermission set @s movement enabled")
                                            source.camera.clear()
                                            source.removeTag("pr:capturing")
                                        }, 0.75 * 20)
                                    }, 0.75 * 20)
                                }, 0.75 * 20)
                            }, 0.75 * 20)
                        }, 0.75 * 20)
                    }, 0.75 * 20)
                }, 0.75 * 20)
            }, 0.75 * 20)
        })
    }
}
// Ja foi
world.afterEvents.entitySpawn.subscribe((event) => {
    const { entity, cause } = event;
    // Entities can become invalid on the same tick they "spawn" (failed spawn / instant despawn / replacement).
    // Guard everything to prevent InvalidEntityError (hasTag/getComponent/etc).
    try {
        if (!entity) return;
        if (typeof entity.isValid === "function" && !entity.isValid()) return;
    } catch {
        return;
    }

    try {
        const typeId = entity.typeId ?? "";
        if (!typeId.startsWith("pr:")) return;
        if (typeId.includes("ball")) return;

        const fam = entity.getComponent("minecraft:type_family");
        if (!fam?.hasTypeFamily?.("pokemon")) return;

        if (entity.getDynamicProperty("pr:tamed") !== undefined) return;

        // Don't auto-remove Mega forms or other special spawns.
        // Mega entities often use different skin_id values and are intentionally summonable.
        if (typeId.includes("mega")) return;
        // If spawned by command/spawn egg, don't treat it as a "wild skin enforcement" spawn.
        try {
            if (cause === "Command" || cause === "SpawnEgg") return;
        } catch { }

        const skin = entity.getComponent("skin_id");
        if (skin && skin.value !== 1) {
            entity.remove();
            return;
        }

        const pokemonData = pokemonsData.find(pokemon => pokemon.id == typeId);
        if (!pokemonData) return;

        entity.setDynamicProperty("pr:tamed", false);
        const health = entity.getComponent("minecraft:health");
        if (health) health.setCurrentValue(pokemonData.maxHealth);

        const level = randomValue(pokemonData.minSpawnLevel, pokemonData.maxSpawnLevel);
        entity.setDynamicProperty("pr:level", level);
        entity.nameTag = formatEntityName(entity) + " Wild\nLvl: §a" + entity.getDynamicProperty("pr:level") + "§r";
    } catch {
        // swallow to avoid breaking the entire script tick if an entity invalidates mid-handler
    }
})
// Ja foi
function formatEntityName(entity) {
    if (!entity || !entity.typeId) return "Unknown";

    let id = entity.typeId;

    if (id.includes(":")) {
        id = id.split(":")[1];
    }

    // Troca _ por espaço
    id = id.replace(/_/g, " ");

    // Deixa a primeira letra de cada palavra maiúscula
    id = id.split(" ").map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");

    return id;
}
// Ja foi
function randomValue(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Ja foi
world.afterEvents.itemStartUseOn.subscribe((event) => {
    const { source, itemStack, block } = event
    if (itemStack?.typeId?.startsWith("pr:") && !itemStack.typeId.includes("ball")) {
        if (itemStack?.getLore()[0]?.includes("Tamed") && itemStack?.getLore()[1]?.includes(source.nameTag) && !itemStack?.getLore()[3]?.includes("Death")) {
            const pokemon = source.dimension.spawnEntity(itemStack.typeId.replace("_spawn_egg", ""), {
                x: block.location.x,
                y: block.location.y + 1,
                z: block.location.z
            })
            const currentHealthValue = pokemon.getComponent("minecraft:health");
            const valueTP = itemStack.getLore()[3].replace("Health:§7 ", "")
            let valueInN = Number(valueTP);
            currentHealthValue.setCurrentValue(valueInN)
            pokemon.getComponent("minecraft:tameable").tame(source)
            pokemon.setDynamicProperty("pr:tamed", true)

            setMovesOnSpawn(source, pokemon)
            pokemon.setDynamicProperty("pr:owner", source.id)
            pokemon.setDynamicProperty("pr:level", itemStack.getLore()[2].replace("Level:§7 ", ""))
            pokemon.nameTag = formatEntityName(pokemon) + " | Lvl: §a" + pokemon.getDynamicProperty("pr:level") + "§r\n" + "Owner:§a " + source.name
        } else if (itemStack?.getLore()[3]?.includes("Death")) {
            __safeActionBar(source, "You can't place a death pokemon!");
        }
        if (itemStack.getLore().length == 0) {
            const pokemonData = pokemonsData.find(pokemon => pokemon.id == itemStack.typeId.replace("_spawn_egg", ""))
return
            if (pokemonData) {
                const pokemon = source.dimension.spawnEntity(itemStack.typeId.replace("_spawn_egg", ""), {
                    x: block.location.x,
                    y: block.location.y + 1,
                    z: block.location.z
                })
                pokemon.setDynamicProperty("pr:tamed", false)
                const currentHealthValue = pokemon.getComponent("minecraft:health");
                currentHealthValue.setCurrentValue(pokemonData.maxHealth)
                //console.warn(currentHealthValue.currentValue)
                const level = randomValue(pokemonData.minSpawnLevel, pokemonData.maxSpawnLevel)
                pokemon.setDynamicProperty("pr:level", level)
                pokemon.nameTag = formatEntityName(pokemon) + " Wild\nLvl: §a" + pokemon.getDynamicProperty("pr:level") + "§r"
            }

        }
    }
})

world.afterEvents.entityDie.subscribe((event) => {
    const { damageSource, deadEntity } = event
    const dimension = deadEntity?.dimension
    const location = deadEntity?.location
    const item = new ItemStack(deadEntity.typeId + "_spawn_egg", 1);
    if (deadEntity.typeId.startsWith("pr:")) {
        if (deadEntity?.getComponent("minecraft:type_family")?.hasTypeFamily("pokemon") && deadEntity?.getDynamicProperty("pr:tamed") == true) {
            if (deadEntity.getDynamicProperty("pr:sended_pokemon") == true) {
                const players = deadEntity.dimension.getPlayers({
                    location: deadEntity.location,
                })
                for (let player of players) {
                    if (player.id == deadEntity.getDynamicProperty("pr:owner")) {
                        player.setDynamicProperty("pr:sended_pokemon_id", undefined)
                        player.setDynamicProperty("pr:sended_pokemon_slot", undefined)
                        player.setDynamicProperty("pr:sended_pokemon", false)
                    }
                }
            }
           // dimension.spawnItem(item, location)
        }
    }
})
// Ja foi
world.afterEvents.playerInteractWithEntity.subscribe((event) => {
    const { player, target, itemStack } = event
    const health = target.getComponent("health")
    if (target.typeId.startsWith("pr:") && !target.typeId.includes("ball") && target.getComponent("minecraft:type_family")?.hasTypeFamily("pokemon")) {
        /*if (player.isSneaking) {
            if (target.nameTag.includes(player.nameTag) && target.getDynamicProperty("pr:sended_pokemon") != true) {
                const item = new ItemStack(target.typeId + "_spawn_egg", 1);
                item.setLore([
                    `${formatEntityName(target)} Tamed`,
                    `Owner:§7 ${player.nameTag}`,
                    `Level:§7 ${target.getDynamicProperty("pr:level")}`,
                    `Health:§7 ${target.getComponent("minecraft:health").currentValue}`
                ])
                player.dimension.spawnItem(item, target.location)
                target.remove()
            }
        } else {*/
            if (itemStack?.typeId == "pr:potion") {
                health.setCurrentValue(health.currentValue + 20)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                __safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:rare_candy") {
                let level = Number(target.getDynamicProperty("pr:level")) || 1;
                const newLevel = level + 1;
                target.setDynamicProperty("pr:level", newLevel);

                // Persist level so sidebar + battles keep the new value (rare candy path)
                let slotIndex = target.getDynamicProperty("pr:slot_index");
                if (slotIndex === undefined || slotIndex === null || isNaN(Number(slotIndex))) {
                    const sentSlot = player.getDynamicProperty("pr:sended_pokemon_slot");
                    if (sentSlot !== undefined && sentSlot !== null && !isNaN(Number(sentSlot))) {
                        slotIndex = Number(sentSlot);
                        if (slotIndex >= 1 && slotIndex <= 6) slotIndex = slotIndex - 1;
                    } else {
                        const sel = player.getDynamicProperty("pr:selectedSlot");
                        if (sel !== undefined && sel !== null && !isNaN(Number(sel))) slotIndex = Number(sel) - 1;
                    }
                } else {
                    slotIndex = Number(slotIndex);
                }

                if (slotIndex !== undefined && slotIndex !== null && slotIndex >= 0 && slotIndex <= 5) {
                    try {
                        const slotData = pokemonUtils.getPokemonFromSlot(player, slotIndex);
                        if (slotData) {
                            slotData.level = newLevel;
                            pokemonUtils.updatePokemonSlot(player, slotIndex, slotData);
                        }
                        player.setDynamicProperty(`pr:slot_${slotIndex + 1}_level`, newLevel);
                    } catch (e) {}

                    // +1 Learn Point per level (rare candy path)
                    const slotNum = slotIndex + 1;
                    try {
                        // mark this slot as "already awarded this tick" to avoid double-award by the generic level watcher
                        try { player.setDynamicProperty(`pr:lp_skip_s${slotNum}`, system.currentTick); } catch (e) { }

                        addLearnPoints(player, slotNum, 1).then((total) => {
                            try { cacheLearnPoints(player, slotNum, total); } catch (e) { }
                            try { player.sendMessage(`§a+1 Learn Point§r (Total: §e${total}§r)`); } catch (e) { }
                        });
                    } catch (e) { }
                }

                // Update nametag
                if (target.getDynamicProperty("pr:nickname") == undefined) {
                    target.nameTag = formatEntityName(target) + " | Lvl: §a" + target.getDynamicProperty("pr:level") + "§r\n" + "Owner:§a " + target.getDynamicProperty("pr:owner_name");
                } else {
                    target.nameTag = target.getDynamicProperty("pr:nickname") + " | Lvl: §a" + target.getDynamicProperty("pr:level") + "§r\n" + "Owner:§a " + target.getDynamicProperty("pr:owner_name");
                }

                // Determine evolution target using data-driven levels
                const evoInfo = pokemonUtils.getEvolutionForPokemon({ typeId: target.typeId, level: newLevel });
                const evoTarget = evoInfo ? evoInfo.targetId : null;

                if (evoTarget) {

                    evoSwapPokemon(player, target, evoTarget, newLevel);
                    // consume candy
                    try { player.runCommand(`clear @s ${itemStack.typeId} 0 1`); } catch (e) {}
                    __safeActionBar(player, "Evolution!");
                    return;
                }

                // consume candy + feedback
                try { player.runCommand(`clear @s ${itemStack.typeId} 0 1`); } catch (e) {}
                safePlaySound(player, "random.orb");
                __safeActionBar(player, "Level up!");
            }
            if (itemStack?.typeId == "pr:super_potion") {
                health.setCurrentValue(health.currentValue + 60)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:hyper_potion") {
                health.setCurrentValue(health.currentValue + 120)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:max_potion") {
                health.setCurrentValue(health.currentValue + 800)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:full_restore") {
                health.setCurrentValue(health.currentValue + 800)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:fresh_water") {
                health.setCurrentValue(health.currentValue + 30)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:soda_pop") {
                health.setCurrentValue(health.currentValue + 50)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:lemonade") {
                health.setCurrentValue(health.currentValue + 70)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:moomoo_milk") {
                health.setCurrentValue(health.currentValue + 100)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:berry_juice") {
                health.setCurrentValue(health.currentValue + 20)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:energy_powder") {
                health.setCurrentValue(health.currentValue + 60)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (itemStack?.typeId == "pr:energy_root") {
                health.setCurrentValue(health.currentValue + 120)
                player.runCommand(`clear @s ${itemStack.typeId} 0 1`)
                safePlaySound(player, "random.orb");__safeActionBar(player, "Healed!")
            }
            if (!itemStack) {
                if (target.nameTag.includes(player.nameTag)) {
                    // Clicked your OWN pokemon with empty hand:
                    // Prompt in chat for mode (avoids UI freeze).
                    const pid = player.id;

                    // If already searching (PVE), cancel like before
                    if (player.getDynamicProperty("pr:looking_for_battle") == true) {
                        player.setDynamicProperty("pr:looking_for_battle", false)
                        target.setDynamicProperty("pr:looking_for_battle", false)
                        safePlaySound(player, "random.orb");__safeActionBar(player, "Battle searching canceled!")
                        return
                    }

                    // If already targeting (PVP), cancel
                    if (__pvpTargeting.has(pid) || player.getDynamicProperty("pr:pvp_targeting") === true) {
                        __pvpTargeting.delete(pid)
                        player.setDynamicProperty("pr:pvp_targeting", false)
                        player.setDynamicProperty("pr:pvp_selected_pokemon", undefined)
                        safePlaySound(player, "random.orb");__safeActionBar(player, "§cPVP targeting canceled!")
                        return
                    }

                    // Set pending choice (player will type !pve or !pvp)
                    __pendingBattleChoice.set(pid, target);

                    safePlaySound(player, "random.orb");__safeActionBar(player, "§bType §f!pve §bor §f!pvp §b(or §f!cancelbattle§b).")
                    player.sendMessage("§7Battle Search: type §a!pve §7for wild battles, or §e!pvp §7to challenge a player. Type §c!cancelbattle §7to cancel.")

} else {
    // If in PVP targeting mode, clicking another player's pokemon sends a challenge
    const pid = player.id;
    const attackerPokemon = __pvpTargeting.get(pid);
    if (attackerPokemon) {
        if (!__entityValid(attackerPokemon)) {
            __pvpTargeting.delete(pid);
            player.setDynamicProperty("pr:pvp_targeting", false);
            __safeActionBar(player, "§cYour selected pokemon is missing.");
        } else {
            const defenderOwnerId = target.getDynamicProperty("pr:owner") || "";
            const defenderOwnerName = target.getDynamicProperty("pr:owner_name") || "";
            if (defenderOwnerId && defenderOwnerId === player.id) {
                __safeActionBar(player, "§cThat\'s your own pokemon.");
                return;
            }

            // Find defender by id first, fallback to name
            const defender = world.getAllPlayers().find(p => p.id === defenderOwnerId)
                || world.getAllPlayers().find(p => p.nameTag === defenderOwnerName || p.name === defenderOwnerName);
            if (!defender) {
                __safeActionBar(player, "§cThat pokemon's owner isn't online.");
                return;
            }

            // Prevent challenging someone already with a pending challenge
            if (__pendingChallenge.has(defender.id)) {
                __safeActionBar(player, "§cThat player already has a pending challenge.");
                return;
            }

            // Store pending challenge on defender
            const expireTick = system.currentTick + (20 * 30); // 30 seconds
            __pendingChallenge.set(defender.id, {
                fromName: player.nameTag,
                fromId: player.id,
                        attackerPokemon: attackerPokemon,
                        defenderPokemon: target,
                expireTick
            });

            // Clear attacker's targeting state (prevents spam)
            __pvpTargeting.delete(pid);
            player.setDynamicProperty("pr:pvp_targeting", false);
            player.setDynamicProperty("pr:pvp_selected_pokemon", undefined);

            safePlaySound(player, "random.orb");
            safePlaySound(defender, "random.orb");

            __safeActionBar(player, `§eChallenge sent to ${defender.nameTag}.`);
            defender.sendMessage(`§e${player.nameTag} challenged you to a battle! Type §a!accept §eor §c!decline §e(30s).`);
            __safeActionBar(defender, "§ePvP challenge received: type !accept or !decline.");

            // Timeout cleanup
            system.runTimeout(() => {
                const pending = __pendingChallenge.get(defender.id);
                if (pending && pending.fromId === player.id && pending.defenderPokemon && pending.defenderPokemon.id === target.id) {
                    __pendingChallenge.delete(defender.id);
                    __safeActionBar(defender, "§cChallenge expired.");
                    __safeActionBar(player, "§cChallenge expired.");
                }
            }, 20 * 30);
            return;
        }
    }

    if (player.getDynamicProperty("pr:looking_for_battle") == true) {
                        const pokemons = player.dimension.getEntities({
                            location: player.location,
                            families: ["pokemon"],
                            maxDistance: 32
                        })
                        for (let pokemon of pokemons) {
                            if (pokemon.nameTag.includes(player.nameTag) && pokemon.getDynamicProperty("pr:looking_for_battle") == true) {
                                safePlaySound(player, "random.orb");player.setDynamicProperty("pr:looking_for_battle", false)
                                pokemon.setDynamicProperty("pr:looking_for_battle", false)
                                pokemon.setDynamicProperty("pr:on_battle", true)
                                target.setDynamicProperty("pr:on_battle", true)
                                battleMenu(player, pokemon, target, 0)
                            }
                        }
                    }
                }
            }
        //}
    }
    if (target.typeId == "pr:pokecenter_healer_machinery") {
        if (itemStack?.getLore()[3]?.includes("Death")) {
            const item = new ItemStack(itemStack.typeId.replace("_fainted", "") + "_spawn_egg", 1);
            const pokemonData = pokemonsData.find(pokemon => pokemon.id == ("pr:" + formatName(itemStack.getLore()[0].replace("Tamed", ""))))

            if (pokemonData) {
                item.setLore([
                    itemStack.getLore()[0],
                    itemStack.getLore()[1],
                    itemStack.getLore()[2],
                    `Health:§7 ${pokemonData.maxHealth}`
                ])
                safePlaySound(player, "random.orb");__safeActionBar(player, itemStack.getLore()[0].replace(" Tamed", "") + " Healed!")
                player.dimension.spawnItem(item, {
                    x: target.location.x,
                    y: target.location.y + 1,
                    z: target.location.z,
                })
                const inv = player.getComponent("equippable")
                if (inv.getEquipment("Mainhand")) {
                    inv.setEquipment("Mainhand", undefined)
                }
            }
        }
    }
})

world.afterEvents.itemUse.subscribe((data) => {
    const { source: player, itemStack: item } = data;
    // Revive items are handled by scripts/allanz/event/item.js (custom item component + Realms-safe fallback).
    // Prevent double-handling here so the revive UI can properly consume the item.
    if (item && (item.typeId === "pr:revive" || item.typeId === "pr:max_revive" || item.typeId === "pr:revival_herb")) return;
});

export function revivePoke(player, health, oldItem) {
    let form = new ActionFormData();
    let functions = [];
    form.title("Options");
    form.body(`000000000000`)
    const inv = player.getComponent("minecraft:inventory").container
    for (let i = 0; i < inv.size; i++) {
        const item = inv.getItem(i);
        if (item) {
            if (item.getLore().length > 0 && item.getLore()[0].includes("Tamed") && item.getLore()[3].includes("Death")) {
                let pokeLevel = item.getLore()[2].replace("Level:§7 ", "")
                const pokemonData = pokemonsData.find(pokemon => pokemon.id == item.typeId.replace("_fainted", ""))
                let pokeMaxHealth = pokemonData.maxHealth

                form.button("§f" + formatItemName(item) + "§7\nLvl.§r" + pokeLevel, "textures/icons/fainted/" + formatItemName2(item));
                functions.push((res) => {
                    const itemNew = new ItemStack(item.typeId.replace("_fainted", "_spawn_egg"), 1);
                    if (health == "max") {
                        itemNew.setLore([
                            item.getLore()[0],
                            item.getLore()[1],
                            item.getLore()[2],
                            `Health:§7 ${pokeMaxHealth}`
                        ])
                    } else {
                        itemNew.setLore([
                            item.getLore()[0],
                            item.getLore()[1],
                            item.getLore()[2],
                            `Health:§7 ${health}`
                        ])
                    }
                    player.runCommand(`clear @s ${oldItem.typeId} 0 1`)
                    inv.setItem(i, itemNew)
                });
            }
        }
    }
    {
        form.button("Close");
        functions.push((res) => {
        });
    }
    form.show(player).then((res) => {
        const canceled = res.canceled;
        if (canceled == true) {
            return;
        }
        functions[res.selection]();
    });
}

// RETURNFIX_V7_LOADED
system.runInterval(() => {
    for (const player of world.getAllPlayers()) {
        try {
            if (player.getDynamicProperty("pr:returnfix_v7_loaded") === true) continue;
            player.setDynamicProperty("pr:returnfix_v7_loaded", true);
            player.sendMessage("§b[PokeReviva] ReturnFix v7 loaded.");
        } catch (e) {}
    }
}, 40);