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

const REQUIRED_KILL_ITEM_ID = "mebesm:kill_sign";
const DEATH_TAG = "DeathMe";

const PULL_BULLETS = new Set([
  "mebesm:grabpack_blue_bullet",
  "mebesm:grabpack_gren_active_bullet",
  "mebesm:grabpack_gren_bullet",
  "mebesm:grabpack_omni_bullet",
  "mebesm:grabpack_pressure_bullet",
  "mebesm:grabpack_purple_bullet",
  "mebesm:grabpack_red_bullet",
  "mebesm:grabpack_yellow_bullet",
  "mebesm:grabpack_yellow_energi_bullet",
  "mebesm:grabpack_yellow_fire_bullet",
  "mebesm:grabpack_yellow_ice_bullet",
]);

const PUSH_BULLETS = new Set([
  "mebesm:grabpack_gren_and_blue_bullet",
  "mebesm:grabpack_omni_and_blue_bullet",
  "mebesm:grabpack_pressure_and_blue_bullet",
  "mebesm:grabpack_purple_and_blue_bullet",
  "mebesm:grabpack_red_and_blue_bullet",
  "mebesm:grabpack_yellow_and_blue_bullet",
]);

const ALL_BULLETS = new Set([...PULL_BULLETS, ...PUSH_BULLETS]);

const CRATE_TYPE = "mebesm:handprint_crate";
const PUSH_DISTANCE = 5;
const PUSH_STEPS = 10;
const DETECT_RADIUS = 2;
const KNOCKBACK_RADIUS = 3;
const KNOCKBACK_FORCE = 2.0;

const processedBullets = new Set();

function isBlockSolid(dimension, x, y, z) {
  try {
    const block = dimension.getBlock({ x, y, z });
    return block && !block.isAir && block.isSolid;
  } catch {
    return true;
  }
}

function knockbackNearby(entity, x, y, z, shooter) {
  try {
    const nearby = entity.dimension.getEntities({
      location: { x, y, z },
      maxDistance: KNOCKBACK_RADIUS
    }).filter(e => {
      if (e.id === entity.id) return false;
      if (shooter && e.id === shooter.id) return false;
      if (e.typeId === CRATE_TYPE) return false;
      return true;
    });

    for (const mob of nearby) {
      if (!mob.isValid()) continue;
      try {
        const mx = mob.location.x - x;
        const my = mob.location.y - y;
        const mz = mob.location.z - z;
        const mlen = Math.sqrt(mx * mx + my * my + mz * mz);
        if (mlen === 0) continue;

        mob.applyImpulse({
          x: (mx / mlen) * KNOCKBACK_FORCE,
          y: KNOCKBACK_FORCE * 0.5,
          z: (mz / mlen) * KNOCKBACK_FORCE
        });
      } catch { }
    }
  } catch { }
}

function moveEntitySafe(entity, targetX, targetY, targetZ, shooter) {
  const ex = entity.location.x;
  const ey = entity.location.y;
  const ez = entity.location.z;

  const dx = targetX - ex;
  const dy = targetY - ey;
  const dz = targetZ - ez;
  const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
  if (len === 0) return;

  const steps = Math.max(PUSH_STEPS, Math.ceil(len * 2));
  const nx = dx / len;
  const ny = dy / len;
  const nz = dz / len;

  let lastX = ex;
  let lastY = ey;
  let lastZ = ez;

  for (let i = 1; i <= steps; i++) {
    const t = (len / steps) * i;
    const tx = ex + nx * t;
    const ty = ey + ny * t;
    const tz = ez + nz * t;

    if (isBlockSolid(entity.dimension, Math.floor(tx), Math.floor(ty), Math.floor(tz)) ||
      isBlockSolid(entity.dimension, Math.floor(tx), Math.floor(ty + 1), Math.floor(tz))) {
      break;
    }

    lastX = tx;
    lastY = ty;
    lastZ = tz;
  }

  entity.teleport(
    { x: lastX, y: lastY, z: lastZ },
    { dimension: entity.dimension }
  );

  knockbackNearby(entity, lastX, lastY, lastZ, shooter);
}

function pushEntitySafe(entity, shooter) {
  const ex = entity.location.x;
  const ey = entity.location.y;
  const ez = entity.location.z;
  const sx = shooter.location.x;
  const sy = shooter.location.y;
  const sz = shooter.location.z;

  const dx = ex - sx;
  const dy = ey - sy;
  const dz = ez - sz;
  const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
  if (len === 0) return;

  const nx = dx / len;
  const ny = dy / len;
  const nz = dz / len;

  const targetX = ex + nx * PUSH_DISTANCE;
  const targetY = ey + ny * PUSH_DISTANCE;
  const targetZ = ez + nz * PUSH_DISTANCE;

  moveEntitySafe(entity, targetX, targetY, targetZ, shooter);
}

function distSq(a, b) {
  const dx = a.x - b.x;
  const dy = a.y - b.y;
  const dz = a.z - b.z;
  return dx * dx + dy * dy + dz * dz;
}

world.beforeEvents.worldInitialize.subscribe(initEvent => {
  const registry = initEvent.blockComponentRegistry;

  registry.registerCustomComponent('myaddon:interactive_block_trigger', {
    onPlayerInteract: e => {
      e.player.runCommandAsync("playsound block.mebesm_mebesm @p");
    },
  });

  registry.registerCustomComponent('mebesm:limon_toy_trigger', {
    onPlayerInteract: e => {
      e.player.runCommandAsync("playsound limon @p");
    },
  });
});

world.afterEvents.entityHitEntity.subscribe(event => {
  const { damagingEntity, hitEntity } = event;

  if (damagingEntity?.typeId !== "minecraft:player" || hitEntity.typeId === "minecraft:player") return;

  const equipment = damagingEntity.getComponent(EntityComponentTypes.Equippable);
  const mainhandItem = equipment?.getEquipment("Mainhand");

  if (mainhandItem?.typeId === REQUIRED_KILL_ITEM_ID && !hitEntity.hasTag(DEATH_TAG)) {
    hitEntity.addTag(DEATH_TAG);
    damagingEntity.runCommand(`execute at @s run playsound random.break @a ~ ~ ~ 0.5 0.8`);
  }
});

system.runInterval(() => {
  const dimensions = ["overworld", "nether", "the_end"];

  for (const dimId of dimensions) {
    try {
      const dimension = world.getDimension(dimId);

      const crates = dimension.getEntities({ type: CRATE_TYPE });
      if (crates.length === 0) continue;

      for (const crate of crates) {
        if (!crate.isValid()) continue;

        const nearbyBullets = dimension.getEntities({
          location: crate.location,
          maxDistance: DETECT_RADIUS + 1
        }).filter(e => ALL_BULLETS.has(e.typeId));

        for (const bullet of nearbyBullets) {
          if (!bullet.isValid()) continue;
          if (processedBullets.has(bullet.id)) continue;

          const d = distSq(crate.location, bullet.location);
          if (d > DETECT_RADIUS * DETECT_RADIUS) continue;

          processedBullets.add(bullet.id);

          let shooter = null;
          try {
            const projComp = bullet.getComponent("minecraft:projectile");
            shooter = projComp?.owner;
          } catch { }

          if (!shooter) continue;

          const bulletId = bullet.typeId;

          if (PULL_BULLETS.has(bulletId)) {
            moveEntitySafe(
              crate,
              shooter.location.x,
              shooter.location.y,
              shooter.location.z,
              shooter
            );
          } else if (PUSH_BULLETS.has(bulletId)) {
            pushEntitySafe(crate, shooter);
          }

          system.runTimeout(() => {
            processedBullets.delete(bullet.id);
          }, 40);
        }
      }
    } catch { }
  }
}, 1);

system.runInterval(() => {
  const dimensions = ["overworld", "nether", "the_end"];

  for (const dimId of dimensions) {
    try {
      const dimension = world.getDimension(dimId);
      const taggedEntities = dimension.getEntities({
        tags: [DEATH_TAG],
        excludeTypes: ["minecraft:player"]
      });

      for (const entity of taggedEntities) {
        if (entity.isValid()) {
          entity.kill();
        }
      }
    } catch { }
  }
}, 5);