import * as mc from "@minecraft/server";
import * as vector from "../utils/vector";
import * as selectors from "../utils/selectors";
import * as boulder from "./boulderProjectile";
import * as timer from "../timer";
import * as music from "../misc/sculptorMusic";
import * as bluffProjectile from "./bluffProjectile";
import { unstuckEntity } from "../items/earthrendGauntlet";

export function sculptorRadius(sculptor, height) {
  const originalHeight = parseFloat(sculptor.getProperty("hf_mzs:start_y"));
  let start = { x: 0, y: height, z: 0 };
  let end = { x: 0, y: originalHeight, z: 0 };
  const dist = vector.distance(start, end);

  if (dist <= 12) return 4;
  if (dist <= 15) return 5;
  if (dist <= 17) return 5;
  if (dist <= 18) return 7;
  if (dist <= 19) return 7;
  if (dist <= 20) return 7;
  if (dist <= 40) return 9;
  if (dist <= 45) return 7;
  return 4;
}

export function boulderRadius(sculptor, height) {
  const originalHeight = parseFloat(sculptor.getProperty("hf_mzs:start_y"));
  let start = { x: 0, y: height, z: 0 };
  let end = { x: 0, y: originalHeight, z: 0 };
  const dist = vector.distance(start, end);

  if (dist <= 5) return 5;
  if (dist <= 17) return 7;
  if (dist <= 25) return 8;
  if (dist <= 30) return 9;
  if (dist <= 40) return 10;
  if (dist <= 45) return 7;
  return 5;
}

export function eventBehavior(event) {
  if (!event.entity.isValid()) return;
  if (event.eventId === "minecraft:entity_spawned") {
    event.entity.triggerEvent("hf_mzs:give_dialogue");
    let startY = Math.floor(event.entity.location.y);
    const bl = event.entity.dimension.getBlockFromRay(event.entity.location, vector.down, {
      maxDistance: 2,
      includeLiquidBlocks: false,
      includePassableBlocks: false,
    });
    if (bl && bl.block && bl.block.isValid() && bl.block.above() && bl.block.above().isValid())
      startY = Math.floor(bl.block.above().y);
    event.entity.setProperty("hf_mzs:start_y", startY);
    event.entity.setDynamicProperty(
      "hf_mzs:start_location",
      `${event.entity.location.x},${startY},${event.entity.location.z}`
    );
    checkBlocks(event.entity, 1);
  }
  if (event.eventId === "hf_mzs:give_dialogue") {
    mc.system.runTimeout(() => {
      if (event.entity && event.entity.isValid()) event.entity.runCommand("dialogue change @s hf_mzs:sculptor_test @a");
    }, 2);
  }
}

export function hurtBehavior(event) {
  try {
    const sculptor = event.hurtEntity;
    const source =
      event.damageSource.damagingEntity ??
      event.damageSource.damagingProjectile.getComponent("minecraft:projectile").owner;
    sculptor.setProperty("hf_mzs:is_hurt", true);
    sculptor.setDynamicProperty("hf_mzs:last_attacker", source.id);

    // if (sculptor.getProperty("hf_mzs:is_testing")) failTest(sculptor);
    mc.system.runTimeout(() => {
      if (sculptor && sculptor.isValid()) sculptor.setProperty("hf_mzs:is_hurt", false);
    }, 5);
  } catch (e) {}
}

export function onDeath(event) {
  if (event.id === "hf_mzs:sculptor_death") {
    const sculptor = event.sourceEntity;
    sculptor.runCommand(`/event entity @e[type=hf_mzs:highlight,tag="${sculptor.id}"] despawn`);
    sculptor.setProperty("hf_mzs:death", true);
    if (sculptor.getProperty("hf_mzs:is_attacking")) endAttack(sculptor);
  }
}

export function interceptProjectile(event) {
  const { projectile } = event;
  const sculptor = event.getEntityHit().entity;
  if (sculptor.typeId === "hf_mzs:sculptor") {
    if (projectile && projectile.isValid() && !projectile.hasTag(sculptor.id)) sculptorRetaliate(sculptor, projectile);
  }
}

export function sculptorRetaliate(sculptor, projectile, isBluffProjectile) {
  if (sculptor.getProperty("hf_mzs:is_testing")) return;
  try {
    if (projectile.isValid() && !sculptor.getProperty("hf_mzs:blocked_projectile")) {
      const vel = isBluffProjectile
        ? vector.normalize(vector.subtract(sculptor.location, projectile.location))
        : projectile.getVelocity();
      const nextPos = vector.add(projectile.location, vel);
      const dist = vector.distance(nextPos, sculptor.location);
      if (dist <= 2) {
        if (projectile.typeId === "hf_mzs:boulder") {
          boulder.onBoulderDestroy(projectile, projectile.location);
        } else {
          projectile.clearVelocity();
          projectile.applyImpulse(vector.multiply(vel, -0.1));
        }
        sculptor.triggerEvent("hf_mzs:block_anim_on");

        let dir;
        const projComponent = projectile.getComponent("minecraft:projectile");
        if (projComponent && projComponent.owner && projComponent.owner.isValid()) {
          dir = vector.multiply(vector.normalize(vector.subtract(projComponent.owner.location, sculptor.location)), -1);
        }
        dir = vector.multiply(vector.normalize(vel), -1.5);
        let pos = vector.add(sculptor.location, dir);
        pos.y += 1;
        const b = sculptor.dimension.spawnEntity("hf_mzs:boulder<hf_mzs:stone_small>", pos);

        let rot = (Math.atan2(dir.x, dir.z) * -180) / Math.PI;
        sculptor.setProperty("hf_mzs:y_rotation", rot);
        sculptor.setRotation({ x: rot, y: 0 });

        mc.system.runTimeout(() => {
          sculptor.runCommand("/playsound hf_mzs:boulder.small.hit @a[r=32] ~ ~ ~ 100 1 1");
          if (!b.isValid()) return;
          b.triggerEvent("hf_mzs:flying");
          b.setProperty("hf_mzs:spawned", true);
          b.setProperty("hf_mzs:launched", true);

          let map = new mc.MolangVariableMap();
          let rot = (Math.atan2(dir.x, dir.z) * -180) / Math.PI;
          map.setFloat("variable.direction", rot);
          map.setFloat("variable.lifetime", 1.0);
          let offset = 0.5;
          map.setFloat("variable.size", 0.5);

          let particleLocation = vector.subtract(b.location, vector.multiply(dir, -0.5));
          particleLocation.y += offset;
          b.dimension.spawnParticle("hf_mzs:ring_grow", particleLocation, map);

          // Recalculate the direction
          if (projComponent && projComponent.isValid() && projComponent.owner && projComponent.owner.isValid()) {
            let targetLocation = projComponent.owner.location;
            targetLocation.y += 1;
            dir = vector.multiply(vector.normalize(vector.subtract(targetLocation, b.location)), 1);
          }
          boulder.shootBoulder(b, vector.multiply(vector.normalize(dir), 0.5));
        }, 12);
      }
    }
  } catch (e) {}
}

export function startAttack(event) {
  if (event.id === "hf_mzs:sculptor_start_attack") {
    const sculptor = event.sourceEntity;

    const target = mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:last_attacker"));

    if (
      sculptor.getComponent("minecraft:health").currentValue > 1 &&
      target &&
      target.isValid() &&
      !sculptor.getProperty("hf_mzs:is_testing")
    ) {
      if (
        target.typeId === "minecraft:player" &&
        target.getGameMode() != mc.GameMode.survival &&
        target.getGameMode() != mc.GameMode.adventure
      )
        return;
      if (!sculptor.getProperty("hf_mzs:is_attacking")) {
        let loc = sculptor.location;
        loc.x = Math.floor(loc.x) + 0.5;
        loc.y = Math.floor(loc.y);
        loc.z = Math.floor(loc.z) + 0.5;
        let pillar = sculptor.dimension.spawnEntity("hf_mzs:stone_pillar", loc);
        pillar.setDynamicProperty("hf_mzs:sculptor", sculptor.id);
        sculptor.setDynamicProperty("hf_mzs:pillar", pillar.id);
        sculptor.setProperty("hf_mzs:is_attacking", true);
        sculptor.setProperty("hf_mzs:descended", false);
        sculptor.setDynamicProperty("hf_mzs:target", target.id);
        sculptor.setDynamicProperty(
          "hf_mzs:attack_origin",
          `${sculptor.location.x},${sculptor.location.y},${sculptor.location.z}`
        );
        sculptor.runCommand("/camerashake add @a[r=32] 0.75 2.0 positional");
        mc.system.runTimeout(() => {
          sculptor.setDynamicProperty("hf_mzs:attack_started", true);
        }, 16);
      }
    } else if (
      target &&
      target.isValid() &&
      !sculptor.getProperty("hf_mzs:is_attacking") &&
      sculptor.getProperty("hf_mzs:is_testing")
    ) {
      music.end(mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:pillar")), false);

      sculptor.setProperty("hf_mzs:is_attacking", true);
      mc.system.runTimeout(() => {
        sculptor.setProperty("hf_mzs:is_testing", false);
      }, 5);
      sculptor.setProperty("hf_mzs:descended", false);

      sculptor.setDynamicProperty("hf_mzs:target", target.id);
      timer.remove(sculptor.getDynamicProperty("hf_mzs:timer"));
      sculptor.setDynamicProperty(
        "hf_mzs:attack_origin",
        `${sculptor.location.x},${sculptor.location.y - 50},${sculptor.location.z}`
      );
      sculptor.setDynamicProperty("hf_mzs:attack_started", true);
      sculptor.triggerEvent("hf_mzs:fail_test");
      boulder.platformToOrbiting(sculptor);

      sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace minecraft:barrier");
      sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_block");
      sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_quarter");
      sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_slab");

      sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace minecraft:barrier");
      sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_block");
      sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
      sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_slab");

      sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace minecraft:barrier");
      sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_block");
      sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
      sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_slab");

      sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace minecraft:barrier");
      sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_block");
      sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
      sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_slab");
    }
  }
}

function endAttack(sculptor, isKilled) {
  sculptor.triggerEvent("hf_mzs:end_attack_sequence");
  const pillar = mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:pillar"));
  pillar.triggerEvent("hf_mzs:lowering");
  sculptor.setProperty("hf_mzs:is_attacking", false);
  mc.system.runTimeout(() => {
    sculptor.runCommand(`/event entity @e[type=hf_mzs:boulder,tag="${sculptor.id}"] hf_mzs:on_hit`);
    if (!isKilled) sculptor.getComponent("minecraft:health").resetToMaxValue();
  }, 2);
}

export async function startTest(event) {
  if (event.id === "hf_mzs:sculptor_start_test") {
    const sculptor = event.sourceEntity;

    if (sculptor.getProperty("hf_mzs:is_attacking")) return;

    await checkBlocks(sculptor, 1, true);
    checkIfObstructed(sculptor);

    mc.system.runTimeout(() => {
      if (sculptor.getProperty("hf_mzs:test_obstructed")) {
        // sculptor.runCommand("/give @p hf_mzs:bluff_rod 1");
        return;
      }

      if (!sculptor.getProperty("hf_mzs:is_testing")) {
        let loc = sculptor.location;
        loc.x = Math.floor(loc.x) + 0.5;
        loc.y = Math.floor(loc.y);
        loc.z = Math.floor(loc.z) + 0.5;

        sculptor.setProperty("hf_mzs:is_testing", true);
        sculptor.setProperty("hf_mzs:descended", false);
        sculptor.runCommand("/camerashake add @a[r=32] 0.75 2.0 positional");

        music.start(sculptor);

        mc.system.runTimeout(() => {
          selectors
            .union(
              {
                location: loc,
                maxDistance: 2,
                excludeFamilies: ["inanimate"],
              },
              ["mob", "player"],
              sculptor.dimension,
              sculptor.id
            )
            .forEach((target) => {
              if (target.isValid()) {
                const dir = vector.subtract(target.location, sculptor.location);
                target.applyKnockback(dir.x, dir.z, 2, 0.3);
              }
            });

          let pillar = sculptor.dimension.spawnEntity("hf_mzs:stone_pillar", loc);
          pillar.setDynamicProperty("hf_mzs:sculptor", sculptor.id);
          sculptor.setDynamicProperty("hf_mzs:pillar", pillar.id);
          const timerId = timer.create(sculptor, sculptor.location);
          sculptor.setDynamicProperty("hf_mzs:timer", timerId);
          mc.world.getEntity(timerId).setDynamicProperty("hf_mzs:sculptor", sculptor.id);
          music.startLoop(pillar);

          mc.system.runTimeout(() => {
            if (sculptor.isValid()) sculptor.setDynamicProperty("hf_mzs:test_started", true);
          }, 13);
          mc.system.runTimeout(() => {
            if (sculptor.isValid()) sculptor.triggerEvent("hf_mzs:allow_interactions");
          }, 274);

          let location1 = {
            x: sculptor.location.x + Math.floor(Math.random() * 3 + 1),
            y: sculptor.location.y + 1,
            z: sculptor.location.z + Math.floor(Math.random() * 3 + 1),
          };
          let location2 = {
            x: sculptor.location.x - Math.floor(Math.random() * 3 + 1),
            y: sculptor.location.y + 1,
            z: sculptor.location.z + Math.floor(Math.random() * 3 + 1),
          };
          let location3 = {
            x: sculptor.location.x + Math.floor(Math.random() * 3 + 1),
            y: sculptor.location.y + 1,
            z: sculptor.location.z - Math.floor(Math.random() * 3 + 1),
          };
          let location4 = {
            x: sculptor.location.x - Math.floor(Math.random() * 3 + 1),
            y: sculptor.location.y + 1,
            z: sculptor.location.z - Math.floor(Math.random() * 3 + 1),
          };
          let location5 = {
            x: sculptor.location.x - Math.floor(Math.random() * 3 + 2),
            y: sculptor.location.y + 1,
            z: sculptor.location.z,
          };
          let location6 = {
            x: sculptor.location.x,
            y: sculptor.location.y + 1,
            z: sculptor.location.z - Math.floor(Math.random() * 3 + 2),
          };
          let location7 = {
            x: sculptor.location.x + Math.floor(Math.random() * 3 + 2),
            y: sculptor.location.y + 1,
            z: sculptor.location.z,
          };
          let location8 = {
            x: sculptor.location.x,
            y: sculptor.location.y + 1,
            z: sculptor.location.z + Math.floor(Math.random() * 3 + 2),
          };

          boulder.spawnPlatform(sculptor, sculptor, sculptor.location.y + 47, sculptor.id, 0, 1, location1);
          boulder.spawnPlatform(sculptor, sculptor, sculptor.location.y + 47, sculptor.id, 0, 1, location2);
          boulder.spawnPlatform(sculptor, sculptor, sculptor.location.y + 47, sculptor.id, 0, 1, location3);
          boulder.spawnPlatform(sculptor, sculptor, sculptor.location.y + 47, sculptor.id, 0, 1, location4);
          if (Math.random() < 0.25)
            boulder.spawnPlatform(sculptor, sculptor, sculptor.location.y + 47, sculptor.id, 0, 1, location5);
          if (Math.random() < 0.25)
            boulder.spawnPlatform(sculptor, sculptor, sculptor.location.y + 47, sculptor.id, 0, 1, location6);
          if (Math.random() < 0.25)
            boulder.spawnPlatform(sculptor, sculptor, sculptor.location.y + 47, sculptor.id, 0, 1, location7);
          if (Math.random() < 0.25)
            boulder.spawnPlatform(sculptor, sculptor, sculptor.location.y + 47, sculptor.id, 0, 1, location8);
        }, 24);
      }
    }, 5);
  }
}

export function endTest(sculptor) {
  // mc.world.sendMessage("endTest");
  music.end(mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:pillar")), false);
  sculptor.runCommand("/tag @a[r=64] remove hf_mzs_sculptor_test");

  sculptor.setProperty("hf_mzs:is_testing", false);

  sculptor.runCommand("/effect @s regeneration 12 12 true");

  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace minecraft:barrier");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_block");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_quarter");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_slab");

  sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace minecraft:barrier");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_block");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_slab");

  sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace minecraft:barrier");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_block");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_slab");

  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace minecraft:barrier");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_block");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_slab");
  sculptor.runCommand(`/event entity @e[type=hf_mzs:boulder,tag="${sculptor.id}"] hf_mzs:on_hit`);

  sculptor.runCommandAsync("/effect @a[r=50] slow_falling 15 1 true");

  const pillar = mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:pillar"));
  // added lines, reset sculptor back to the top even if he hasnt reached it yet
  if (pillar !== undefined && pillar.isValid())
    pillar.runCommand("/fill ~-1 ~ ~-1 ~1 ~47 ~1 hf_mzs:invisible_block [] replace air");
  sculptor.teleport(vector.add(pillar.location, vector.multiply(vector.up, 48)));

  pillar.triggerEvent("hf_mzs:lowering");
  timer.remove(sculptor.getDynamicProperty("hf_mzs:timer"));

  mc.system.runTimeout(() => {
    sculptor.getComponent("minecraft:health").resetToMaxValue();
  }, 2);
}

export function endTestOnDeath(event) {
  const entity = event.deadEntity;
  if (entity && entity.isValid() && entity.typeId === "minecraft:player") {
    // mc.world.sendMessage("endTestOnDeath");
    entity.removeTag("hf_mzs_sculptor_test");
    entity.runCommand("/stopsound @s hf_mzs:music.sculptor.music");
    const loc = entity.location;
    const s = entity.dimension.getEntities({ location: loc, type: "hf_mzs:sculptor", maxDistance: 96 });
    for (const sculptor of s) {
      const target = mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:target"));

      if (target && target.isValid()) {
        if (target.id === entity.id) {
          endAttack(sculptor);
        }
      }
    }
  }
}

export function endTestInstant(event) {
  const sculptor = event.sourceEntity;
  if (sculptor === undefined || !sculptor.isValid()) return;
  if (event.id !== "hf_mzs:end_test_instant") return;
  sculptor.runCommand("/tag @a[r=64] remove hf_mzs_sculptor_test");
  // mc.world.sendMessage("endTestInstant");

  sculptor.setProperty("hf_mzs:is_testing", false);
  const pillar = mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:pillar"));
  if (pillar !== undefined) {
    music.end(pillar);
    sculptor.teleport(vector.add(pillar.location, vector.up));
    pillar.runCommand("/fill ~-1 ~ ~-1 ~1 ~50 ~1 air [] replace hf_mzs:invisible_block");
    pillar.triggerEvent("hf_mzs:down");
  }

  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_block");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_quarter");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~16 ~5 ~-2 air [] replace hf_mzs:invisible_slab");

  sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_block");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~-16 ~16 ~5 ~16 air [] replace hf_mzs:invisible_slab");

  sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_block");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
  sculptor.runCommandAsync("/fill ~2 ~-55 ~2 ~-16 ~5 ~16 air [] replace hf_mzs:invisible_slab");

  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_block");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_quarter");
  sculptor.runCommandAsync("/fill ~-16 ~-55 ~-16 ~-2 ~5 ~16 air [] replace hf_mzs:invisible_slab");
  sculptor.runCommand(`/event entity @e[type=hf_mzs:boulder,tag="${sculptor.id}"] hf_mzs:on_hit`);

  timer.remove(sculptor.getDynamicProperty("hf_mzs:timer"));
}

export function giveGauntlet(event) {
  if (event.id === "hf_mzs:sculptor_give_gauntlet") {
    const sculptor = event.sourceEntity;
    sculptor.runCommand(`/event entity @e[type=hf_mzs:highlight,tag="${sculptor.id}"] despawn`);
    sculptor.runCommand(`/event entity @e[type=hf_mzs:boulder,tag="${sculptor.id}"] hf_mzs:on_hit`);
    sculptor.runCommand("/tp @s ~~~ facing @p");
    const dir = vector.multiply(vector.flatten(sculptor.getViewDirection(), "y"), 0.4);
    const item = new mc.ItemStack("hf_mzs:earthrend_gauntlet");
    const iEntity = sculptor.dimension.spawnItem(item, vector.add(sculptor.location, vector.up));
    iEntity.applyImpulse(dir);
    sculptor.setProperty("hf_mzs:gauntlet", false);
    mc.system.runTimeout(() => {
      const loc = sculptor.location;
      loc.y += 1.25;
      sculptor.dimension.spawnParticle("hf_mzs:sculptor_disappear", loc);
    }, 20);
  }
}

export function testPassed(event) {
  if (event.id === "hf_mzs:sculptor_pass_test") {
    passTest(event.sourceEntity);
  }
}

function failTest(sculptor) {
  if (sculptor && sculptor.isValid()) {
    // mc.world.sendMessage("failTest");
    sculptor.runCommand("/tag @a[r=64] remove hf_mzs_sculptor_test");
    sculptor.setProperty("hf_mzs:passed_test", false);
    sculptor.triggerEvent("hf_mzs:fail_test");
    music.end(mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:pillar")), false);
    endTest(sculptor);
  }
}

function passTest(sculptor) {
  //mc.world.sendMessage("pass");
  sculptor.runCommand("/tag @a[r=64] remove hf_mzs_sculptor_test");
  sculptor.setProperty("hf_mzs:passed_test", true);
  music.end(mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:pillar")), true);
  endTest(sculptor);
}

export function tick1() {
  try {
    const getSculptors = mc.world.getDimension("overworld").getEntities({ type: "hf_mzs:sculptor" });
    const getSculptorsN = mc.world.getDimension("nether").getEntities({ type: "hf_mzs:sculptor" });
    const getSculptorsE = mc.world.getDimension("the_end").getEntities({ type: "hf_mzs:sculptor" });

    getSculptors.push(...getSculptorsN, ...getSculptorsE);

    for (let sculptor of getSculptors) {
      const entities = sculptor.dimension.getEntities({ location: sculptor.location, maxDistance: 20 });
      for (const projectile of entities) {
        if (projectile && projectile.isValid()) {
          const projectileComponent = projectile.getComponent("minecraft:projectile");
          if (projectileComponent) {
            const e = projectile.dimension.getEntitiesFromRay(projectile.location, projectile.getVelocity(), {
              maxDistance: 20,
              type: "hf_mzs:sculptor",
            })[0];
            if (e) {
              const en = e.entity;
              if (en.isValid() && en.id === sculptor.id) {
                sculptorRetaliate(sculptor, projectile, false);
              }
            }
          }
          if (projectile.typeId === "hf_mzs:bluff_projectile") {
            if (vector.distance(projectile.location, sculptor.location) < 1.5) {
              sculptorRetaliate(sculptor, projectile, true);
              bluffProjectile.finish(projectile, projectile.location);
            }
          }
        }
      }

      // end test on ender pearl
      const ep = sculptor.dimension.getEntities({
        location: sculptor.location,
        maxDistance: 64,
        type: "minecraft:ender_pearl",
      });
      if (sculptor.getProperty("hf_mzs:is_testing") && ep[0] !== undefined) endTest(sculptor);

      if (sculptor.getDynamicProperty("hf_mzs:attack_started") || sculptor.getDynamicProperty("hf_mzs:test_started")) {
        const pillar = mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:pillar") ?? "0");
        if (pillar && pillar.isValid()) {
          const pillarStep = pillar.getComponent("minecraft:variant");
          if (pillarStep.value === 1) {
            sculptor.runCommand("/tp @s ~ ~0.1995 ~");
          } else if (pillarStep.value === 2) {
            if (!sculptor.hasTag("hf_mzs_teleported")) {
              sculptor.teleport(vector.add(pillar.location, vector.multiply(vector.up, 48)));
              sculptor.addTag("hf_mzs_teleported");
            }
          } else if (pillarStep.value === 0) {
            sculptor.removeTag("hf_mzs_teleported");
            sculptor.setDynamicProperty("hf_mzs:attack_started", false);
            sculptor.setDynamicProperty("hf_mzs:test_started", false);
            sculptor.setProperty("hf_mzs:descended", true);
            timer.remove(sculptor.getDynamicProperty("hf_mzs:timer"));
            pillar.remove();
          }
        }
      }

      if (sculptor.getProperty("hf_mzs:is_attacking")) {
        const target = mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:target"));

        if (!target || !target.isValid()) continue;

        let dir = vector.subtract(target.location, sculptor.location);

        let rot = (Math.atan2(dir.x, dir.z) * -180) / Math.PI;
        sculptor.setProperty("hf_mzs:y_rotation", rot);
        sculptor.setRotation({ x: rot, y: 0 });
      }

      if (!sculptor.getProperty("hf_mzs:is_testing")) {
        sculptor.triggerEvent("hf_mzs:deny_interactions");
      }

      checkIfObstructed(sculptor);
    }
  } catch (e) {}
}

export function tick5() {
  const getSculptors = mc.world.getDimension("overworld").getEntities({ type: "hf_mzs:sculptor" });
  const getSculptorsN = mc.world.getDimension("nether").getEntities({ type: "hf_mzs:sculptor" });
  const getSculptorsE = mc.world.getDimension("the_end").getEntities({ type: "hf_mzs:sculptor" });

  getSculptors.push(...getSculptorsN, ...getSculptorsE);

  for (let sculptor of getSculptors) {
    try {
      if (!sculptor || !sculptor.isValid()) continue;
      if (sculptor.getProperty("hf_mzs:is_attacking") && sculptor.getDynamicProperty("hf_mzs:attack_started")) {
        const boulders = sculptor.dimension.getEntities({
          location: sculptor.location,
          type: "hf_mzs:boulder",
          tags: [sculptor.id],
        });
        if (boulders.length < 50) {
          spawnAttackBoulder(sculptor);
        }
      }

      const pos = sculptor.location;
      const startY = parseFloat(sculptor.getProperty("hf_mzs:start_y"));
      pos.y = startY - 1;
      const block = sculptor.dimension.getBlock(pos);
      if (!block || (block && !block.isValid()) || (block && block.isValid() && (block.isAir || block.isLiquid))) {
        const b = sculptor.dimension.spawnEntity(`hf_mzs:boulder<hf_mzs:sculptor_test_medium>`, pos);
        b.addTag(sculptor.id);
        boulder.fillBlocks(b, 1);

        if (sculptor.location.y < startY)
          sculptor.teleport({ x: sculptor.location.x, y: startY + 0.25, z: sculptor.location.z });
      }
    } catch {}

    if (
      !sculptor.getProperty("hf_mzs:is_testing") &&
      !sculptor.getProperty("hf_mzs:is_attacking") &&
      sculptor.getProperty("hf_mzs:descended")
    ) {
      let c = sculptor.getDynamicProperty("hf_mzs:start_location");
      if (c) {
        c = c.split(",");
        const tpPos = { x: parseFloat(c[0]), y: parseFloat(c[1]), z: parseFloat(c[2]) };
        sculptor.teleport(tpPos);

        const bl1 = sculptor.dimension.getBlock(tpPos);
        tpPos.y += 1;
        const bl2 = sculptor.dimension.getBlock(tpPos);
        if (bl1 && bl1.isValid()) bl1.setPermutation(mc.BlockPermutation.resolve("minecraft:air"));
        if (bl2 && bl2.isValid()) bl2.setPermutation(mc.BlockPermutation.resolve("minecraft:air"));
      }
    }
  }
}

export function tick20() {
  const getSculptors = mc.world.getDimension("overworld").getEntities({ type: "hf_mzs:sculptor" });
  const getSculptorsN = mc.world.getDimension("nether").getEntities({ type: "hf_mzs:sculptor" });
  const getSculptorsE = mc.world.getDimension("the_end").getEntities({ type: "hf_mzs:sculptor" });

  getSculptors.push(...getSculptorsN, ...getSculptorsE);

  for (let sculptor of getSculptors) {
    if (sculptor.isValid())
      sculptor.addEffect("slow_falling", 50, {
        amplifier: 10,
        showParticles: false,
      });
    if (sculptor.getProperty("hf_mzs:is_attacking")) {
      const boulders = sculptor.dimension.getEntities({
        location: sculptor.location,
        type: "hf_mzs:boulder",
        tags: [sculptor.id],
      });
      if (boulders.length > 0) {
        const target = mc.world.getEntity(sculptor.getDynamicProperty("hf_mzs:target"));
        if (!target || !target.isValid() || vector.distance(target.location, sculptor.location) > 64) {
          // if (target && target.typeId === "minecraft:player" && target.getGameMode() !== mc.GameMode.survival && target.getGameMode() !== mc.GameMode.adventure) continue;
          endAttack(sculptor);
          return;
        }
        if (
          target &&
          target.typeId === "minecraft:player" &&
          target.getGameMode() !== mc.GameMode.survival &&
          target.getGameMode() !== mc.GameMode.adventure
        ) {
          endAttack(sculptor);
          return;
        }
        if (sculptor.getComponent("minecraft:health").currentValue === 1) {
          endAttack(sculptor, true);
          return;
        }
        const b = boulders[Math.floor(Math.random() * boulders.length)];

        sculptor.runCommand("/playsound hf_mzs:boulder.small.hit @a[r=64] ~ ~ ~ 100 1 1");
        b.triggerEvent("hf_mzs:flying");
        b.setProperty("hf_mzs:spawned", true);
        b.setProperty("hf_mzs:launched", true);
        b.clearVelocity();

        // Recalculate the direction
        let dir = vector.normalize(vector.subtract(target.location, b.location));

        let map = new mc.MolangVariableMap();
        let rot = (Math.atan2(dir.x, dir.z) * -180) / Math.PI;
        map.setFloat("variable.direction", rot);
        map.setFloat("variable.lifetime", 0.4);
        let offset = 0.75;

        let markVariant = b.getComponent("minecraft:mark_variant");
        switch (markVariant.value) {
          case 0:
            map.setFloat("variable.size", 0.6);
            offset = 0.5;
            b.dimension.playSound("hf_mzs:boulder.small.hit", b.location, { volume: 1.0, pitch: 1.0 });
            // b.dimension.playSound("hf_mzs:magic_hit", b.location, { volume: 0.1, pitch: 0.85 });
            break;
          case 1:
            map.setFloat("variable.size", 0.8);
            b.dimension.playSound("hf_mzs:boulder.medium.hit", b.location, { volume: 1.0, pitch: 0.85 });
            // b.dimension.playSound("hf_mzs:magic_hit", b.location, { volume: 0.1, pitch: 0.8 });
            break;
          case 2:
            map.setFloat("variable.size", 1.0);
            b.runCommand("/camerashake add @a[r=32] 0.5 0.25 positional");
            b.dimension.playSound("hf_mzs:boulder.large.hit", b.location, { volume: 1.0, pitch: 0.8 });
            // b.dimension.playSound("hf_mzs:magic_hit", b.location, { volume: 0.1, pitch: 0.7 });
            break;
          case 3:
            map.setFloat("variable.size", 1.2);
            b.runCommand("/camerashake add @a[r=32] 0.75 0.25 positional");
            b.dimension.playSound("hf_mzs:boulder.giant.hit", b.location, { volume: 1.0, pitch: 0.7 });
            // b.dimension.playSound("hf_mzs:magic_hit", b.location, { volume: 0.1, pitch: 0.6 });
            break;
        }

        let particleLocation = vector.subtract(b.location, vector.multiply(dir, 3));
        particleLocation.y += offset;
        b.dimension.spawnParticle("hf_mzs:ring_grow", particleLocation, map);

        boulder.shootBoulder(b, vector.multiply(vector.normalize(dir), 0.5));
      }
    }

    if (sculptor.getProperty("hf_mzs:is_testing")) {
      const players = sculptor.dimension.getEntities({
        location: sculptor.location,
        maxDistance: 128,
        type: "minecraft:player",
      });
      let playersInRange = 0;
      let deadPlayers = 0;

      for (const player of players) {
        const loc = sculptor.location;
        const pLoc = player.location;
        loc.y = pLoc.y;

        if (vector.distance(loc, pLoc) <= 20) {
          player.addTag("hf_mzs_sculptor_test");
          playersInRange++;
        } else {
          player.removeTag("hf_mzs_sculptor_test");
        }

        if (!player.isValid()) deadPlayers++;

        if (vector.distance(loc, pLoc) <= 1.3) unstuckEntity(player);
      }

      if (playersInRange === 0) {
        // mc.world.sendMessage("No players");
        failTest(sculptor);
        continue;
      }

      if (deadPlayers > 0 && deadPlayers === playersInRange) {
        // mc.world.sendMessage("Dead players");
        failTest(sculptor);
        continue;
      }
    }

    // Check if there are players nearby for the idle animations
    const players = sculptor.dimension.getEntities({
      location: sculptor.location,
      maxDistance: 6,
      type: "minecraft:player",
    });
    sculptor.setProperty("hf_mzs:player_nearby", players.length > 0);
  }
}

export function tick100() {
  const getSculptors = mc.world.getDimension("overworld").getEntities({ type: "hf_mzs:sculptor" });
  const getSculptorsN = mc.world.getDimension("nether").getEntities({ type: "hf_mzs:sculptor" });
  const getSculptorsE = mc.world.getDimension("the_end").getEntities({ type: "hf_mzs:sculptor" });

  getSculptors.push(...getSculptorsN, ...getSculptorsE);

  for (let sculptor of getSculptors) {
    if (
      !sculptor.getProperty("hf_mzs:is_testing") &&
      !sculptor.getProperty("hf_mzs:is_attacking") &&
      sculptor.getProperty("hf_mzs:descended")
    )
      checkBlocks(sculptor, 1);
  }
}

export function detectCheating(event) {
  const { block } = event;
  for (const sculptor of block.dimension.getEntities({
    type: "hf_mzs:sculptor",
    location: block.location,
    maxRadius: 128,
  })) {
    // cheating while test is running
    if (!sculptor.isValid()) continue;
    if (sculptor.getProperty("hf_mzs:is_testing")) {
      const source = sculptor.location;
      source.x = Math.floor(source.x);
      source.y = Math.floor(source.y);
      source.z = Math.floor(source.z);
      for (let currY = 5; currY >= -55; currY--) {
        const min = vector.add(source, {
          x: -sculptorRadius(sculptor, source.y + currY),
          y: currY,
          z: -sculptorRadius(sculptor, source.y + currY),
        });
        const max = vector.add(source, {
          x: sculptorRadius(sculptor, source.y + currY),
          y: currY,
          z: sculptorRadius(sculptor, source.y + currY),
        });
        const bounds = { min, max };
        if (locationWithinBounds(block.location, bounds)) {
          // mc.world.sendMessage("Placed block");
          failTest(sculptor);
        }
      }
    }
    if (event.brokenBlockPermutation === undefined) {
      // cheating before test has started (green highlight on blocks)
      const source = sculptor.location;
      source.x = Math.floor(source.x);
      source.y = Math.floor(source.y);
      source.z = Math.floor(source.z);
      for (let currY = 100; currY >= 1; currY--) {
        const min = vector.add(source, {
          x: -sculptorRadius(sculptor, source.y + currY),
          y: currY,
          z: -sculptorRadius(sculptor, source.y + currY),
        });
        const max = vector.add(source, {
          x: sculptorRadius(sculptor, source.y + currY),
          y: currY,
          z: sculptorRadius(sculptor, source.y + currY),
        });
        const bounds = { min, max };
        if (locationWithinBounds(block.location, bounds)) {
          const ent = sculptor.dimension.getEntities({
            location: vector.add(block.location, { x: 0.5, y: 0, z: 0.5 }),
            maxDistance: 0.3,
            families: ["hf_mzs_highlight"],
          });
          if (ent[0] === undefined) {
            let hl = sculptor.dimension.spawnEntity(
              "hf_mzs:highlight",
              vector.add(block.location, { x: 0.5, y: 0, z: 0.5 })
            );
            hl.addTag(sculptor.id);
          }
        }
      }
    }
  }
}

export async function checkBlocks(sculptor, iteration, isFast) {
  if (iteration > 50 || !sculptor || !sculptor.isValid()) return;
  // mc.world.sendMessage(`${iteration}`);

  const source = sculptor.location;
  source.x = Math.floor(source.x);
  source.y = Math.floor(source.y);
  source.z = Math.floor(source.z);
  const radius = sculptorRadius(sculptor, source.y + iteration);
  const min = vector.add(source, { x: -radius, y: iteration, z: -radius });
  const max = vector.add(source, { x: radius, y: iteration, z: radius });
  const bounds = { min, max };
  for (let i = -radius; i <= radius; i++) {
    for (let j = -radius; j <= radius; j++) {
      const blockPos = { x: source.x + i, y: source.y + iteration, z: source.z + j };
      if (blockPos.y <= sculptor.dimension.heightRange.min || blockPos.y >= sculptor.dimension.heightRange.max)
        continue;
      const block = sculptor.dimension.getBlock(blockPos);
      if (block && block.isValid() && !block.isAir && !block.isLiquid && locationWithinBounds(block.location, bounds)) {
        const ent = sculptor.dimension.getEntities({
          location: vector.add(block.location, { x: 0.5, y: 0, z: 0.5 }),
          maxDistance: 0.3,
          families: ["hf_mzs_highlight"],
        });
        if (ent[0] === undefined) {
          let hl = sculptor.dimension.spawnEntity(
            "hf_mzs:highlight",
            vector.add(block.location, { x: 0.5, y: 0, z: 0.5 })
          );
          hl.addTag(sculptor.id);
        }
      }
    }
  }

  if (!isFast) {
    mc.system.runTimeout(async () => {
      await checkBlocks(sculptor, iteration + 1);
    }, 1);
  } else {
    await checkBlocks(sculptor, iteration + 1);
  }
}

export function checkIfObstructed(sculptor) {
  // Detect if test obstructed
  const highlights = sculptor.dimension.getEntities({
    location: sculptor.location,
    maxDistance: 48,
    tags: [sculptor.id],
    families: ["hf_mzs_highlight"],
  });

  if (highlights.length === 0 && sculptor.getProperty("hf_mzs:test_obstructed"))
    sculptor.setProperty("hf_mzs:test_obstructed", false);
  else if (highlights.length > 0 && !sculptor.getProperty("hf_mzs:test_obstructed"))
    sculptor.setProperty("hf_mzs:test_obstructed", true);
}

function spawnAttackBoulder(sculptor) {
  let o = sculptor.getDynamicProperty("hf_mzs:attack_origin");
  o = o.split(",");
  const attackOrigin = { x: parseFloat(o[0]), y: parseFloat(o[1]), z: parseFloat(o[2]) };
  attackOrigin.y += Math.floor(Math.random() * 46 + 4);
  const xOffset =
    Math.random() * (-sculptorRadius(sculptor, attackOrigin.y) - sculptorRadius(sculptor, attackOrigin.y)) +
    sculptorRadius(sculptor, attackOrigin.y);
  const zOffset =
    Math.random() * (-sculptorRadius(sculptor, attackOrigin.y) - sculptorRadius(sculptor, attackOrigin.y)) +
    sculptorRadius(sculptor, attackOrigin.y);
  attackOrigin.x += xOffset;
  attackOrigin.z += zOffset;
  boulder.spawnOrbiting(sculptor, attackOrigin, attackOrigin.y - 1);
}

function locationWithinBounds(location, bounds) {
  return (
    location.x >= bounds.min.x &&
    location.x <= bounds.max.x &&
    location.y >= bounds.min.y &&
    location.y <= bounds.max.y &&
    location.z >= bounds.min.z &&
    location.z <= bounds.max.z
  );
}
