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

const SWAP_DELAY = 20;
const SWAP_LOCK_TAG = "pr:mega_swapping";
const BASE_ID_DP = "pr:mega_base_form";

// Optional: if your script engine supports beforeEvents, we cancel damage.
// If not, we apply temporary resistance to prevent “vanish before swap”.
function tryCancelHitDamage() {
  try {
    world.beforeEvents.entityHitEntity.subscribe((ev) => {
      const player = ev.damagingEntity;
      const target = ev.hitEntity;
      if (!player || player.typeId !== "minecraft:player") return;
      if (!target || !target.typeId?.startsWith("pr:")) return;

      const held = player.getComponent("minecraft:equippable")?.getEquipment("Mainhand");
      if (!held || !held.typeId?.startsWith("pr:mega_stone_")) return;

      ev.cancel = true;
    });
  } catch {
    // engine doesn't support beforeEvents.entityHitEntity
  }
}
tryCancelHitDamage();

function cloneLoc(loc) {
  return { x: loc.x, y: loc.y, z: loc.z };
}

function playMegaParticle(entity) {
  try {
    entity.dimension.spawnParticle("pr:mega_evolution", entity.location);
  } catch {}
}

function safeInvuln(ent, ticks) {
  try {
    ent.addEffect("resistance", ticks, { amplifier: 255, showParticles: false });
  } catch {}
}

function getHealth(ent) {
  try {
    return ent.getComponent("minecraft:health")?.currentValue;
  } catch {
    return undefined;
  }
}
function setHealth(ent, value) {
  try {
    const h = ent.getComponent("minecraft:health");
    if (!h || value === undefined) return;
    h.setCurrentValue(Math.max(1, Math.min(value, h.defaultValue)));
  } catch {}
}

function copyTags(fromEnt, toEnt) {
  try {
    for (const t of fromEnt.getTags()) toEnt.addTag(t);
  } catch {}
}

// Try to copy all dynamic properties (works on newer engine builds)
function copyDynamicProps(fromEnt, toEnt) {
  try {
    const ids = fromEnt.getDynamicPropertyIds?.() ?? [];
    for (const id of ids) {
      try {
        toEnt.setDynamicProperty(id, fromEnt.getDynamicProperty(id));
      } catch {}
    }
  } catch {}
}

function tryApplyTame(toEnt, player) {
  // Your pack uses a "tamed" event/component-group pattern on many entities:
  try { toEnt.triggerEvent?.("tamed"); } catch {}

  // Also attempt vanilla tame if supported by this entity + API:
  try {
    const tameable = toEnt.getComponent?.("minecraft:tameable");
    if (tameable?.tame) tameable.tame(player);
  } catch {}
}

function spawnFirstValid(dimension, location, candidates) {
  for (const id of candidates) {
    try {
      return dimension.spawnEntity(id, location);
    } catch {}
  }
  return null;
}

// Core swap helper: spawn new, copy state, remove old next tick
function doSwap(oldEnt, dimension, location, newIdCandidates, player, preserveBaseId) {
  // prevent double triggers
  try {
    if (oldEnt.hasTag?.(SWAP_LOCK_TAG)) return null;
    oldEnt.addTag?.(SWAP_LOCK_TAG);
  } catch {}

  const oldNameTag = oldEnt.nameTag;
  const oldHp = getHealth(oldEnt);

  // Spawn new entity first (so we don't lose the old if spawn fails)
  const newEnt = spawnFirstValid(dimension, location, newIdCandidates);
  if (!newEnt) return null;

  // Copy state
  copyTags(oldEnt, newEnt);
  copyDynamicProps(oldEnt, newEnt);
  try { newEnt.nameTag = oldNameTag; } catch {}
  tryApplyTame(newEnt, player);
  setHealth(newEnt, oldHp);

  // Preserve base-form id for reliable reverts
  if (preserveBaseId && typeof preserveBaseId === "string") {
    try { newEnt.setDynamicProperty(BASE_ID_DP, preserveBaseId); } catch {}
  }

  // Remove old next tick (avoids invalid entity errors in other listeners)
  system.run(() => {
    try { oldEnt.remove(); } catch {}
  });

  return newEnt;
}

world.afterEvents.entityHitEntity.subscribe((event) => {
  const player = event.damagingEntity;
  const target = event.hitEntity;

  if (!player || player.typeId !== "minecraft:player") return;
  if (!target || !target.typeId?.startsWith("pr:")) return;

  const held = player.getComponent("minecraft:equippable")?.getEquipment("Mainhand");
  if (!held || !held.typeId?.startsWith("pr:mega_stone_")) return;

  // Don’t let the mon die/vanish during the delay on engines where we can't cancel damage
  safeInvuln(target, SWAP_DELAY + 10);

  const id = target.typeId;

  // Capture NOW (before any other system removes the entity)
  const dim = target.dimension;
  const loc = cloneLoc(target.location);
  const oldNameTag = target.nameTag; // used if oldEnt invalid later

  // Particle first
  playMegaParticle(target);

  system.runTimeout(() => {
    // -------- REVERT (mega -> base) --------
    if (id.startsWith("pr:mega_")) {
      // Prefer stored base id
      let baseId;
      try { baseId = target.getDynamicProperty(BASE_ID_DP); } catch {}
      if (!baseId || typeof baseId !== "string") {
        // fallback guess
        const megaName = id.replace("pr:mega_", "").replace(/_x$/, "").replace(/_y$/, "");
        baseId = `pr:${megaName}`;
      }

      // If target is already invalid, we still attempt to spawn base at saved dim/loc
      let base = null;
      try {
        // attempt full swap (best: copies tags/dp from target)
        base = doSwap(target, dim, loc, [baseId], player, null);
      } catch {
        // fallback: spawn only
        base = spawnFirstValid(dim, loc, [baseId]);
        if (base) {
          try { base.nameTag = oldNameTag; } catch {}
          tryApplyTame(base, player);
        }
      }

      if (!base) {
        player.sendMessage("§cNo base form found.");
      } else {
        player.sendMessage("§dReverted to base form!");
      }
      return;
    }

    // -------- EVOLVE (base -> mega) --------
    const name = id.replace("pr:", "");

    // Try X/Y first if sneaking, otherwise prefer X then Y then normal mega
    const candidates = player.isSneaking
      ? [`pr:mega_${name}_y`, `pr:mega_${name}_x`, `pr:mega_${name}`]
      : [`pr:mega_${name}_x`, `pr:mega_${name}_y`, `pr:mega_${name}`];

    const mega = doSwap(target, dim, loc, candidates, player, id);

    if (!mega) {
      player.sendMessage("§cNo Mega form found.");
    } else {
      player.sendMessage("§dMega Evolution activated!");
    }
  }, SWAP_DELAY);
});