import { system, world, BlockVolume, ItemStack, BlockPermutation } from "@minecraft/server";
import { moveEntityInLoop } from "../utilities/rotating_entities_around_location";
import { getBlockDistance } from "../utilities/helpers";

const cursedCoreComponent = {
  onTick({ block }) {
    const dimension = world.getDimension(block.dimension.id);

    const aboveFlag = isCursedLog(block.above());
    const belowFlag = isCursedLog(block.below());
    let nearbyLostSouls = dimension.getEntities({
      location: block.location,
      type: "hb_hr:lost_soul",
      maxDistance: 15,
    });

    // if cursed core is active and there are no lost souls around the spawn new binded lost souls
    if (aboveFlag && belowFlag && nearbyLostSouls.length === 0) {
      spawnLostSouls(block);
    }

    // move binded lost souls wether the cursed core is active or inactive
    nearbyLostSouls.forEach((entity) => {
      moveEntityInLoop(entity, block);
    });
  },
};

const customComponents = {
  "hb_hr:cursed_core": cursedCoreComponent,
};

export function lostSoulTooFar(player) {
  let nearbyLostSouls = player.dimension.getEntities({
    location: player.location,
    type: "hb_hr:lost_soul",
    maxDistance: 30,
  });

  // if cursed core is active and there are no lost souls around the spawn new binded lost souls
  if (nearbyLostSouls.length === 0) return;
  else {
    nearbyLostSouls.forEach((entity) => {
      if (!entity || !entity.isValid()) return;

      const coreCoords = entity.getDynamicProperty("hb_hr_core_coords");

      if (!coreCoords) {
        try {
          entity.dimension.spawnParticle("hb_hr:lost_soul_state_change", entity.location);
        } catch (error) {}
        entity.dimension.spawnEntity("hb_hr:agressive_lost_soul", entity.location);
        entity.remove();
        return;
      }
      const dist = getBlockDistance(entity.location, coreCoords);
      if (dist.toFixed(3) > 9) {
        try {
          entity.dimension.spawnParticle("hb_hr:lost_soul_state_change", entity.location);
        } catch (error) {}
        entity.dimension.spawnEntity("hb_hr:agressive_lost_soul", entity.location);
        entity.remove();
        return;
      }
      let searchedBlock = player.dimension.getBlock(coreCoords);
      if (!searchedBlock) {
        try {
          entity.dimension.spawnParticle("hb_hr:lost_soul_state_change", entity.location);
        } catch (error) {}
        entity.dimension.spawnEntity("hb_hr:agressive_lost_soul", entity.location);
        entity.remove();
      }
      if (searchedBlock.typeId != "hb_hr:cursed_core") {
        try {
          entity.dimension.spawnParticle("hb_hr:lost_soul_state_change", entity.location);
        } catch (error) {}
        entity.dimension.spawnEntity("hb_hr:agressive_lost_soul", entity.location);
        entity.remove();
      }
    });
  }
}

export function creaking_heart() {
  world.beforeEvents.worldInitialize.subscribe(({ blockComponentRegistry }) => {
    for (const [componentName, componentImplementation] of Object.entries(customComponents)) {
      blockComponentRegistry.registerCustomComponent(componentName, componentImplementation);
    }
  });
  world.afterEvents.playerPlaceBlock.subscribe((event) => {
    const block = event.block;
    const player = event.player;
  });
  world.afterEvents.playerBreakBlock.subscribe((event) => {
    const block = event.brokenBlockPermutation;
    const usedItem = event.itemStackAfterBreak;
    const player = event.player;
    let blockItem = "empty";
    if (block.type.id == "hb_hr:cursed_core") {
      let nearbyLostSouls = world.getDimension(player.dimension.id).getEntities({
        location: event.block.center(),
        type: "hb_hr:lost_soul",
        maxDistance: 10,
      });
      player.dimension.spawnParticle("hb_hr:destroy_active_cursed_core", event.block.center());
      if (nearbyLostSouls.length === 0 || nearbyLostSouls[0] == undefined) {
      } else {
        nearbyLostSouls.forEach((entity) => {
          const allTags = entity.getTags();
          const agressive_lost_soul = entity.getProperty("hb_hr:agressive");
          if (!agressive_lost_soul) {
            try {
              entity.dimension.spawnParticle("hb_hr:lost_soul_state_change", entity.location);
            } catch (error) {}
            const agressive_lost_soul = entity.dimension.spawnEntity("hb_hr:agressive_lost_soul", entity.location);
            agressive_lost_soul.runCommand("/event entity @s hb_hr:can_be_hurt");
          }
          entity.runCommand("/event entity @e[type=hb_hr:agressive_lost_soul,r=100,tag=" + allTags[0] + "] hb_hr:can_be_hurt");
          entity.remove();
        });
      }
    }
  });
  world.afterEvents.entityHitEntity.subscribe((event) => {
    const damager = event.damagingEntity;
    const hit_entity = event.hitEntity;
    if (damager.typeId == "hb_hr:agressive_lost_soul") damager.runCommand("/event entity @s hb_hr:attack_counter");
  });
  system.afterEvents.scriptEventReceive.subscribe((event) => {
    if (event.sourceEntity == undefined) return;
    else {
      if (event.sourceEntity.typeId.includes("hb_hr:") && event.sourceEntity.typeId.includes("lost_soul")) {
        let lost_soul = event.sourceEntity;
        switch (event.id) {
          case "hb_hr:spawn_agressive_lost_soul":
            try {
              lost_soul.dimension.spawnParticle("hb_hr:lost_soul_state_change", lost_soul.location);
            } catch (error) {}
            const agressive_lost_soul = lost_soul.dimension.spawnEntity("hb_hr:agressive_lost_soul", lost_soul.location);
            lost_soul.addTag("hb_hr:" + lost_soul.id);
            agressive_lost_soul.addTag("hb_hr:" + lost_soul.id);
            lost_soul.setProperty("hb_hr:agressive", true);
            break;
          case "hb_hr:reset_lost_soul":
            try {
              lost_soul.dimension.spawnParticle("hb_hr:lost_soul_state_change", lost_soul.location);
            } catch (error) {}
            lost_soul.setProperty("hb_hr:agressive", false);
            break;
          case "hb_hr:reset_origin_lost_soul":
            const allTags = lost_soul.getTags();
            try {
              lost_soul.dimension.spawnParticle("hb_hr:lost_soul_state_change", lost_soul.location);
            } catch (error) {}
            lost_soul.runCommand("/event entity @e[type=hb_hr:lost_soul,r=100,tag=" + allTags[0] + "] hb_hr:reset_lost_soul");
            lost_soul.remove();
            break;
          default:
            break;
        }
      }
    }
  });
}

function spawnLostSouls(block) {
  const locationOffset = 5;
  const dim = world.getDimension(block.dimension.id);
  const center = block.location;

  // 1) Eight waypoints in a circle (Indices 0–7)
  const waypoints = [
    { x: locationOffset, z: -locationOffset }, // 0
    { x: locationOffset * 1.5, z: 0 }, // 1
    { x: locationOffset, z: locationOffset }, // 2
    { x: 0, z: locationOffset * 1.5 }, // 3
    { x: -locationOffset, z: locationOffset }, // 4
    { x: -locationOffset * 1.5, z: 0 }, // 5
    { x: -locationOffset, z: -locationOffset }, // 6
    { x: 0, z: -locationOffset * 1.5 }, // 7
  ];

  // 2) Random number of souls (4–8)
  const soulCount = Math.floor(Math.random() * 5) + 4;

  // 3) Shuffle the indices 0–7
  const indices = [...Array(8).keys()];
  for (let i = indices.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [indices[i], indices[j]] = [indices[j], indices[i]];
  }
  const selected = indices.slice(0, soulCount);

  // 4) For each soul: spawn + waypoints with jitter
  selected.forEach((startIdx) => {
    // Spawn point with its own small jitter
    const spawnBase = waypoints[startIdx];
    const spawnPos = {
      x: center.x + spawnBase.x + Math.random() - 0.5,
      y: center.y,
      z: center.z + spawnBase.z + Math.random() - 0.5,
    };
    let soul;
    try {
      soul = dim.spawnEntity("hb_hr:lost_soul", spawnPos);
    } catch (error) {}

    // Rotate route so it starts at the spawn point
    const ordered = waypoints.slice(startIdx).concat(waypoints.slice(0, startIdx));

    // Assign each waypoint its own jitter
    ordered.forEach((wpBase, idx) => {
      if (soul) {
        soul.setDynamicProperty(`hb_hr_coords${idx + 1}`, {
          x: center.x + wpBase.x,
          y: center.y,
          z: center.z + wpBase.z,
        });
        soul.setDynamicProperty(`hb_hr_core_coords`, {
          x: block.center().x,
          y: block.center().y,
          z: block.center().z,
        });
      }
    });
  });
}

function isCursedLog(block) {
  return block?.typeId === "hb_hr:cursed_log";
}
