import { world, system } from "@minecraft/server";
import { torchHitBlock, torchHitEntity } from "./gadgets/torchLauncher.js";
import {
  witheredArrowBlock,
  witheredArrowEntity,
} from "./trinkets/trinketEffectFunctions.js";
import { safeTeleport } from "./nonSolidBlockLists.js";
import { SHOCKWAVE_PROJECTILES } from "./gadgets/handleShockwave.js";

const notValid = [
  // This add-on
  "pb_gat:grappling_hook_projectile",
  "pb_gat:mb_gam_lever_entity",
  "pb_gat:mb_gam_entity",
  "pb_gat:flight_dummy",
  "pb_gat:mb_tinker_table_entity",
  "pb_gat:mb_tt_item_entity",
  "pb_gat:mb_up_item_entity",
  "pb_gat:mb_upgrade_puncher_entity",
  "pb_gat:shockwave_projectile",
  "pb_gat:spike_dummy",
  "pb_gat:torch_projectile",
  "pb_gat:withered_arrow_dummy",
  // Vanilla
  "minecraft:area_effect_cloud",
  "minecraft:armor_stand",
  "minecraft:arrow",
  "minecraft:breeze_wind_charge_projectile",
  "minecraft:dragon_fireball",
  "minecraft:egg",
  "minecraft:ender_crystal",
  "minecraft:ender_dragon",
  "minecraft:ender_pearl",
  "minecraft:eye_of_ender_signal",
  "minecraft:fireball",
  "minecraft:fireworks_rocket",
  "minecraft:fishing_hook",
  "minecraft:lightning_bolt",
  "minecraft:lingering_potion",
  "minecraft:llama_spit",
  "minecraft:npc",
  "minecraft:ominous_item_spawner",
  "minecraft:shulker_bullet",
  "minecraft:small_fireball",
  "minecraft:snowball",
  "minecraft:splash_potion",
  "minecraft:thrown_trident",
  "minecraft:tripod_camera",
  "minecraft:wind_charge_projectile",
  "minecraft:wither_skull_dangerous",
  "minecraft:wither_skull",
  "minecraft:wither",
  "minecraft:xp_bottle",
];

world.afterEvents.entitySpawn.subscribe((data) => {
  const entity = data.entity;
  const cause = data.cause; // can be born, spawned, loaded, transformed, or dataent, which we may use?
  const owner = entity.getComponent("projectile")?.owner;

  if (entity.typeId === "minecraft:arrow") {
    if (owner?.typeId != "minecraft:player") return;
    if (
      owner.hasTag("pb_gat_trinket_boss_wither_1") ||
      owner.hasTag("pb_gat_trinket_ruinous_blossom_1")
    ) {
      entity.addTag("pb_gat_withered");
      owner.playSound("mob.wither.shoot", {
        pitch: 1.0,
        volume: 0.7,
        location: owner.location,
      });
    }
  }

  if (entity.typeId === "minecraft:ender_pearl") {
    if (
      !owner.hasTag("pb_gat_trinket_boss_dragon_1") &&
      !owner.hasTag("pb_gat_trinket_light_supernova_1")
    )
      return;

    const tpLocationRaw = owner.getDynamicProperty("pb_gat_target_tp_block");
    // If no valid stored location, let the pearl behave normally
    if (!tpLocationRaw) return;
    let tpLocation;
    try {
      tpLocation = JSON.parse(tpLocationRaw);
    } catch {
      return; // Malformed data, allow normal behavior
    }

    if (!tpLocation || typeof tpLocation.x !== "number") return;

    let destination = {
      x: tpLocation.x + 0.5,
      y: tpLocation.y + 1,
      z: tpLocation.z + 0.5,
    };
    const tpBlock = entity.dimension.getBlock(destination);
    if (!safeTeleport.includes(tpBlock.above(0.5)?.typeId)) {
      const playerLoc = owner.location;

      // Determine offset direction
      const dx = playerLoc.x - destination.x;
      const dz = playerLoc.z - destination.z;

      let xOffset = 0;
      let zOffset = 0;

      // Only apply offset on the dominant axis
      if (Math.abs(dx) > Math.abs(dz)) {
        xOffset = dx > 0 ? 1 : -1;
      } else {
        zOffset = dz > 0 ? 1 : -1;
      }

      destination = {
        x: tpLocation.x + 0.5 + xOffset,
        y: tpLocation.y + 0.2,
        z: tpLocation.z + 0.5 + zOffset,
      };
    }

    owner.teleport(destination, {
      dimension: owner.dimension,
      checkForBlocks: true,
    });

    system.runTimeout(() => {
      owner.playSound("mob.endermen.portal", {
        pitch: 1.0,
        volume: 1.0,
        location: destination,
      });
      owner.dimension.spawnParticle("pb_gat:pearl_teleport", destination);
    }, 1);

    // Clear after use
    owner.setDynamicProperty("pb_gat_target_tp_block", undefined);

    // Cancel the pearl
    entity.remove();
  }
});

system.runInterval(() => {
  let arrows = world
    .getDimension("overworld")
    .getEntities({ type: "minecraft:arrow" });
  arrows = arrows.concat(
    world.getDimension("nether").getEntities({ type: "minecraft:arrow" })
  );
  arrows = arrows.concat(
    world.getDimension("the_end").getEntities({ type: "minecraft:arrow" })
  );

  arrows.forEach((arrow) => {
    if (arrow.hasTag("pb_gat_withered")) {
      const velocity = arrow.getVelocity();
      const isStill =
        Math.abs(velocity.x) < 0.01 &&
        Math.abs(velocity.y) < 0.01 &&
        Math.abs(velocity.z) < 0.01;
      if (!isStill) {
        try {
          arrow.dimension.spawnParticle(
            "pb_gat:withered_arrow_trail",
            arrow.location
          );
        } catch (error) {}
      }
    }
  });
});

world.afterEvents.projectileHitBlock.subscribe((data) => {
  const projectile = data.projectile;

  if (projectile.typeId === "pb_gat:torch_projectile") {
    torchHitBlock(data, projectile);
  }
  if (projectile.typeId === "pb_gat:grappling_hook_projectile") {
    try {
      projectile.addTag("pb_gat_hook_hit");
    } catch (error) {}
  }
  if (
    projectile.typeId === "minecraft:arrow" &&
    projectile.hasTag("pb_gat_withered")
  ) {
    witheredArrowBlock(data, projectile);
  }

  if (SHOCKWAVE_PROJECTILES.has(projectile)) {
    const data = SHOCKWAVE_PROJECTILES.get(projectile);
    const location = projectile.location;
    const dimension = projectile.dimension;

    const entities = dimension.getEntities({
      location,
      maxDistance: data.radius,
    });

    for (const entity of entities) {
      if (
        !entity.isValid() ||
        entity.id === data.player.id ||
        notValid.includes(entity.typeId)
      )
        continue;

      const dx = entity.location.x - location.x;
      const dz = entity.location.z - location.z;

      const dir = Math.sqrt(dx * dx + dz * dz);
      const dirX = dir !== 0 ? dx / dir : 0;
      const dirZ = dir !== 0 ? dz / dir : 0;

      try {
        entity.applyKnockback(dirX, dirZ, data.power, data.height);
        entity.dimension.spawnParticle(
          "pb_gat:shockwave_target",
          entity.location
        );

        if (data.shock) {
          entity.addEffect("slowness", 40, {
            amplifier: 5,
            showParticles: false,
          });

          system.runTimeout(() => {
            try {
              dimension.spawnParticle(
                "pb_gat:shockwave_freeze",
                entity.location
              );
            } catch (error) {}
          }, 15);
        }
      } catch (e) {}
    }

    projectile.remove();
  }
});

world.afterEvents.projectileHitEntity.subscribe((data) => {
  const projectile = data.projectile;
  const attacker = data.source;
  const victim = data.getEntityHit().entity;

  if (projectile.typeId === "pb_gat:torch_projectile") {
    torchHitEntity(data);
  }

  if (
    projectile.typeId === "minecraft:arrow" &&
    (attacker?.hasTag("pb_gat_trinket_boss_wither_1") ||
      attacker?.hasTag("pb_gat_trinket_ruinous_blossom_1"))
  ) {
    witheredArrowEntity(data, projectile, attacker);
  }

  if (attacker?.hasTag("pb_has_power") && victim) {
    victim.applyDamage(2);
  }
  if (attacker?.hasTag("pb_has_nitrogen") && victim) {
    victim.applyDamage(2);
    victim.addEffect("slowness", 100, {
      amplifier: 2,
      showParticles: true,
    });
  }
  if (
    projectile.typeId === "pb_gat:grappling_hook_projectile" &&
    projectile?.hasTag("pb_has_barbed")
  ) {
    if (notValid.includes(victim.typeId)) {
      return;
    }
    const ownerTag = projectile
      .getTags()
      .find((tag) => tag.startsWith("owner:"));
    if (!ownerTag) return;

    const playerId = ownerTag.split(":")[1];
    const attacker = world.getAllPlayers().find((p) => p.id === playerId);
    if (!attacker) {
      return;
    }

    let ticks = 0;
    const maxTicks = 12;

    const intervalId = system.runInterval(() => {
      if (!projectile.isValid() || !victim?.isValid() || !attacker?.isValid()) {
        system.clearRun(intervalId);
        return;
      }

      const playerLoc = attacker.location;
      const victimLoc = victim.location;

      const dx = playerLoc.x - victimLoc.x;
      const dy = playerLoc.y - victimLoc.y;
      const dz = playerLoc.z - victimLoc.z;

      const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

      if (distance < 1.5) {
        // Stop pulling when close enough
        projectile.triggerEvent("pb_gat:despawn");
        projectile.triggerEvent("pb_gat:broken_lead");
        system.clearRun(intervalId);
        return;
      }

      const direction = {
        x: dx / distance,
        y: dy / distance,
        z: dz / distance,
      };

      const strength = Math.min(0.12, distance * 0.06); // slightly stronger
      let lift = 0.08; // base lift to negate ground friction

      if (dy > 0.2) {
        // Player is clearly above → add more lift
        lift += Math.min(dy * 0.07, 0.1); // max +0.2 extra
      }
      victim.applyImpulse({
        x: direction.x * strength,
        y: direction.y * strength + lift,
        z: direction.z * strength,
      });

      projectile.applyImpulse({
        x: direction.x * strength,
        y: direction.y * strength + lift,
        z: direction.z * strength,
      });

      ticks++;

      if (ticks >= maxTicks) {
        projectile.triggerEvent("pb_gat:despawn");
        projectile.triggerEvent("pb_gat:broken_lead");
        system.clearRun(intervalId);
      }
    }, 1);
  }
  try {
    if (SHOCKWAVE_PROJECTILES.has(projectile)) {
      const data = SHOCKWAVE_PROJECTILES.get(projectile);
      const location = projectile.location;
      const dimension = projectile.dimension;

      const entities = dimension.getEntities({
        location,
        maxDistance: data.radius,
      });

      for (const entity of entities) {
        if (
          !entity.isValid() ||
          entity.id === data.player.id ||
          notValid.includes(entity.typeId)
        )
          continue;

        const dx = entity.location.x - location.x;
        const dz = entity.location.z - location.z;

        const dir = Math.sqrt(dx * dx + dz * dz);
        const dirX = dir !== 0 ? dx / dir : 0;
        const dirZ = dir !== 0 ? dz / dir : 0;

        try {
          entity.applyKnockback(dirX, dirZ, data.power, data.height);
          entity.dimension.spawnParticle(
            "pb_gat:shockwave_target",
            entity.location
          );

          if (data.shock) {
            entity.addEffect("slowness", 40, {
              amplifier: 5,
              showParticles: false,
            });

            system.runTimeout(() => {
              try {
                dimension.spawnParticle(
                  "pb_gat:shockwave_freeze",
                  entity.location
                );
              } catch (error) {}
            }, 15);
          }
        } catch (e) {}
      }

      projectile.remove();
    }
  } catch (error) {}
});
