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


const HOOK_SPEED = 5;
const LEAD_SPEED = 0.5;
const OFFSET_MULTIPLIER = 4;
let intervalFunction = null;
const data = {};

function distance(a, b) {
  return Math.sqrt(distanceSquared(a, b));
}

function distanceSquared(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;
}

function getDirection(a, b) {
  const vector = {
    x: b.x - a.x,
    y: b.y - a.y,
    z: b.z - a.z,
  };
  const len = Math.sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z);
  return {
    x: vector.x / len,
    y: vector.y / len,
    z: vector.z / len,
  };
}

function fromRotation(rotation) {
  let rotationH = rotation.y * -1;
  let z0 = Math.cos(rotationH * Math.PI / 180);
  let x0 = Math.sin(rotationH * Math.PI / 180);

  let rotationV = rotation.x * -1;
  let h = Math.cos(rotationV * Math.PI / 180);
  let v = Math.sin(rotationV * Math.PI / 180);

  return {
    x: h * x0,
    y: v,
    z: h * z0
  };
}

// Función para iniciar el intervalo
function HookInterval() {
  if (intervalFunction !== null) return;

  intervalFunction = system.runInterval(async () => {
    if (Object.keys(data).length === 0) {
      system.clearRun(intervalFunction);
      intervalFunction = null;
      return;
    }

    for (const [key, hook] of Object.entries(data)) {
      const projectile = hook.projectile;
      const lead = hook.lead;
      const player = hook.player;

      try {
        if (!projectile.hasTag("aft_soulhunters:hook_hitted")) {
          const traveledDistance = distance(projectile.location, hook.startLocation);
          if (traveledDistance >= hook.targetDistance) {
            projectile.clearVelocity();
            projectile.triggerEvent("aft_soulhunters:hooked");
            projectile.addTag("aft_soulhunters:hook_hitted");
            projectile.teleport(hook.targetLocation);
            lead.getComponent("minecraft:rideable").addRider(player);

            // Nueva ubicación de inicio
            hook.startLocation = lead.location;

            // Nueva distancia al objetivo
            hook.targetDistance = distance(hook.startLocation, hook.targetLocation);

            // Dirección hacia el objetivo
            hook.targetDirection = getDirection(hook.startLocation, hook.targetLocation);

            // Ocultar HUD del caballo
            player.onScreenDisplay.setHudVisibility(
              HudVisibility.Hide,
              [
                HudElement.HorseHealth,
              ]
            );

            // Mostrar HUD del jugador
            player.onScreenDisplay.setHudVisibility(
              HudVisibility.Reset,
              [
                HudElement.Armor,
                HudElement.Health,
                HudElement.ProgressBar,
                HudElement.Hunger
              ]
            );
          } else {
            const projectileComponent = projectile.getComponent('minecraft:projectile');
            projectileComponent?.shoot({
              x: hook.targetDirection.x * HOOK_SPEED,
              y: hook.targetDirection.y * HOOK_SPEED,
              z: hook.targetDirection.z * HOOK_SPEED
            });

            // Rotar el proyectil del gancho
            await projectile.runCommandAsync(`tp @s ~ ~ ~ facing @p`);

            // Teletransportar el portador a la posición del jugador
            lead.teleport(player.location);
            continue;
          }
        }

        if (lead.getComponent("minecraft:rideable").getRiders().length == 0) {
          lead.triggerEvent("aft_soulhunters:despawn");
          projectile.triggerEvent("aft_soulhunters:despawn");

          player.applyKnockback(0, 0, 0, 0.5);

          // Detener sonidos
          await player.runCommandAsync("stopsound @s aft_soulhunters:hook_pulling")

          // Restablecer HUD
          system.runTimeout(() => player.onScreenDisplay.setHudVisibility(HudVisibility.Reset), 2);

          delete data[key];
          continue;
        }

        if (lead.hasTag("aft_soulhunters:hooked"))
          continue;

        // Disparar portador hacia la ubicación objetivo
        const traveledDistance = distance(lead.location, hook.startLocation);
        if (traveledDistance >= hook.targetDistance - 1) {
          const directionToOrigin = getDirection(hook.targetLocation, hook.startLocation);
          const targetLocation = {
            x: hook.targetLocation.x + directionToOrigin.x * 1,
            y: hook.targetLocation.y + directionToOrigin.y - 0.5,
            z: hook.targetLocation.z + directionToOrigin.z * 1
          };

          lead.clearVelocity();
          lead.addTag("aft_soulhunters:hooked");
          lead.teleport(targetLocation);

          // Detener sonidos
          await player.runCommandAsync("stopsound @s aft_soulhunters:hook_pulling");

          // Reproducir efecto de sonido
          system.runTimeout(() => {
            if (projectile.isValid()) projectile.triggerEvent("aft_soulhunters:despawn");
            if (lead.isValid()) lead.triggerEvent("aft_soulhunters:despawn");
          }, 6);
          continue;
        }

        // Aplicar fuerza en dirección objetivo
        const projectileComponent = lead.getComponent('minecraft:projectile');
        projectileComponent?.shoot({
          x: hook.targetDirection.x * LEAD_SPEED,
          y: hook.targetDirection.y * LEAD_SPEED,
          z: hook.targetDirection.z * LEAD_SPEED
        });

        // Reproducir efectos de sonido
        player.playSound('aft_soulhunters:hook_pulling', { volume: 0.62, pitch: 1 });

      } catch {
        try { projectile.triggerEvent("aft_soulhunters:despawn"); } catch { }
        try { lead.triggerEvent("aft_soulhunters:despawn"); } catch { }

        // Detener sonidos
        await player.runCommandAsync("stopsound @s aft_soulhunters:hook_pulling");

        // Restablecer HUD
        system.runTimeout(() => player.onScreenDisplay.setHudVisibility(HudVisibility.Reset), 2);
        delete data[key];
      }
    }

    // Si después de procesar los ganchos, no quedan activos, detenemos el intervalo
    if (Object.keys(data).length === 0) {
      system.clearRun(intervalFunction);
      intervalFunction = null;
    }
  });
}

world.beforeEvents.worldInitialize.subscribe(initEvent => {
  initEvent.itemComponentRegistry.registerCustomComponent("aft_soulhunters:grappling_hook", {
    onUse: event => {
      const item = event.itemStack;
      const player = event.source;
      const cooldown = item.getComponent('minecraft:cooldown')
      if (player.getGameMode() !== "creative") {
        cooldown.startCooldown(player)
      } else {
        player.startItemCooldown('grappling_hook', 6);
      }

      try {
        // Predecir la posición del jugador basada en la velocidad
        const predictedLocation = {
          x: player.getHeadLocation().x - player.getVelocity().x * OFFSET_MULTIPLIER,
          y: player.getHeadLocation().y - player.getVelocity().y * OFFSET_MULTIPLIER,
          z: player.getHeadLocation().z - player.getVelocity().z * OFFSET_MULTIPLIER
        };

        // Lanzar rayo en la dirección de visión del jugador
        const blockHit = player.dimension.getBlockFromRay(
          predictedLocation,
          player.getViewDirection(),
          {
            maxDistance: 50
          }
        );

        const tooFarMessages = [
          "That's too far.",
          "I don't do miracles.",
          "Even spiders have limits."
        ];

        if (blockHit === undefined) {
          const randomMessage = tooFarMessages[Math.floor(Math.random() * tooFarMessages.length)];
          player.onScreenDisplay.setActionBar(randomMessage);
          player.startItemCooldown("grappling_hook", 0);
          return;
        }


        // Verificar si el jugador ya está enganchado
        for (const [key, hook] of Object.entries(data)) {
          if (hook.player !== player)
            continue;

          hook.lead.triggerEvent("aft_soulhunters:despawn");
          projectile.triggerEvent("aft_soulhunters:despawn");
          delete data[key];
        }

        // Obtener la ubicación del impacto
        const block = blockHit.block;
        const targetLocation = {
          x: block.location.x + blockHit.faceLocation.x,
          y: block.location.y + blockHit.faceLocation.y,
          z: block.location.z + blockHit.faceLocation.z
        };

        // Obtener la ubicación de aparición del proyectil
        const projectileLocation = player.getHeadLocation();
        const rotation = player.getRotation();

        // Ajustar la rotación para el offset
        rotation.y += 25;
        const offset = fromRotation(rotation);
        projectileLocation.x += offset.x * 1;
        projectileLocation.y += offset.y * 1;
        projectileLocation.z += offset.z * 1;

        // player.startItemCooldown('grappling_hook', 60)
        // Generar el proyectil
        const dimension = player.dimension;
        const projectile = dimension.spawnEntity("aft_soulhunters:hook", projectileLocation);
        projectile.runCommandAsync(`tp @s ~ ~ ~ facing @p`);
        const projectileComponent = projectile.getComponent('minecraft:projectile');
        projectileComponent.owner = player;

        // Esperar al siguiente tick para detectar la entidad generada
        player.playSound('random.bow');

        system.runTimeout(async () => {
          try {
            const lead = dimension.getEntities({
              type: "aft_soulhunters:hook_seat",
              location: projectileLocation,
              maxDistance: 2,
              closest: 1,
              excludeTags: ["aft_soulhunters_hooked"]
            })[0];
            lead.triggerEvent("aft_soulhunters:rideable");
            lead.addTag("aft_soulhunters_hooked");

            data[projectile.id] = {
              player: player,
              projectile: projectile,
              lead: lead,
              startLocation: projectileLocation,
              targetLocation: targetLocation,
              targetDirection: player.getViewDirection(),
              targetDistance: distance(projectile.location, targetLocation)
            };

            // Iniciar el intervalo si no está ya en ejecución
            HookInterval();
            reduceDurability(player, item);
          } catch { }
        }, 1);
      } catch { }
    }
  })

  async function reduceDurability(player, item) {
    if (player.getGameMode() === "creative" || !item) return;

    const durabilityComponent = item.getComponent("durability");
    if (!durabilityComponent) return;

    const enchantable = item.getComponent("minecraft:enchantable");
    const unbreakingLevel = enchantable?.getEnchantment("unbreaking")?.level ?? 0;

    let amount = durabilityComponent.getDamageChance(unbreakingLevel);
    amount += Math.floor(Math.random() * 11) - 5;

    // Prevenir que el daño exceda el máximo
    durabilityComponent.damage = Math.min(
      durabilityComponent.damage + amount,
      durabilityComponent.maxDurability
    );

    const playerInventory = player.getComponent("inventory").container;
    let damagedItem = undefined;

    if (durabilityComponent.damage < durabilityComponent.maxDurability)
      damagedItem = item.clone();

    if (!damagedItem)
      player.runCommandAsync("playsound random.break @s");

    playerInventory.setItem(player.selectedSlotIndex, damagedItem);
  }

})