import { system, world } from "@minecraft/server";
import "./sneakAfterEvent.js";
import "./jumpAfterEvent.js";
import {
  Vec3Multiply,
  Vec3Add,
  Vec3Scale,
  Vec3Normalize,
  applyImpulse,
  Vec3Subtract,
  Vec3Magnitude,
  clamp,
} from "./utils";
import { launchPlayerUp } from "./creatures/drowned_horror";
import { ShowMutantsUI } from "./ui";

world.getAllPlayers().forEach((player) => {
  player.setDynamicProperty("bps_nc:gliding", undefined);
  player.setDynamicProperty("bps_nc:burrowing", undefined);
});

system.afterEvents.scriptEventReceive.subscribe((data) => {
  if (data.id === "bps_nc:launchPlayerUp") {
    launchPlayerUp(data.sourceEntity);
  }
  if (data.id === "bps_nc:shadow_lunge") {
    let dragon = data.sourceEntity;
    let goal_vector = Vec3Multiply(dragon.getViewDirection(), { x: 4.5, y: 0.0, z: 4.5 });
    goal_vector = Vec3Add(goal_vector, { x: 0, y: 0.5, z: 0 });
    dragon.applyImpulse(goal_vector);
  }
  if (data.id === "bps_nc:disappear_ability") {
    disappearAbility(data.sourceEntity);
  }
  if (data.id === "bps_nc:suck_attack") {
    suckAttack(data.sourceEntity);
  }
});

world.afterEvents.playerSpawn.subscribe((data) => {
  const { initialSpawn, player } = data;
  if (!initialSpawn) {
    player.runCommand("/give @s[tag=!bps_nc_no_book] bps_nc:guidebook");
  }
});

world.afterEvents.itemUse.subscribe((data) => {
  const { itemStack: item, source: player } = data;

  if (item.typeId === "bps_nc:guidebook") ShowMutantsUI(player);
});
world.afterEvents.onPlayerSneak.subscribe((data) => {
  const player = data.player;
  const helmet = player.getComponent("minecraft:equippable").getEquipment("Head");
  const boots = player.getComponent("minecraft:equippable").getEquipment("Feet");
  switch (helmet?.typeId) {
    case "bps_nc:creature_4_hat":
      teleportToEntityInView(player, helmet);
      break;
  }
  switch (boots?.typeId) {
    case "bps_nc:dune_walker_boots":
      burrowUnderground(player);
      break;
  }
});
world.afterEvents.onPlayerJump.subscribe((data) => {
  const player = data.player;
  const chest = player.getComponent("minecraft:equippable").getEquipment("Chest");
  if (
    player.isFalling &&
    chest &&
    chest.typeId === "bps_nc:shadow_raptor_wings" &&
    !player.getDynamicProperty("bps_nc:gliding")
  ) {
    activateGliding(player);
  }
});
world.beforeEvents.playerInteractWithEntity.subscribe((data) => {
  const item = data.itemStack;
  const target = data.target;
  const player = data.player;
  system.run(() => {
    if (item?.typeId === "bps_nc:noface_pearl") {
      morphIntoHorror(player, target);
    }
  });
});
/* ------------------------------------------------------------------------------------ */
function disappearAbility(mob) {
  const player = mob.dimension.getEntities({
    closest: 1,
    maxDistance: 100,
    location: mob.location,
    type: "minecraft:player",
  })[0];
  if (!player) {
    return;
  }
  const distanceForTp = mob.getProperty("bps_nc:disappear_distance") * 10 + 3;
  const randomVector = Vec3Normalize({ x: Math.random() * 2 - 1, y: 0, z: Math.random() * 2 - 1 });
  const randomLocation = Vec3Add(player.location, Vec3Scale(randomVector, distanceForTp));
  const teleportLocation = mob.dimension.getTopmostBlock(randomLocation, player.location.y + 20).above().location;
  mob.tryTeleport(teleportLocation);
  player.playSound("bps_nc.creature4.teleport", { volume: 1, pitch: 1 });
}
export function suckAttack(mob, range = 20, speed = 0.15, duration = 10) {
  const pullInterval = system.runInterval(() => {
    if (!mob.isValid() || !mob.isInWater) {
      system.clearRun(pullInterval);
    }
    const entities = mob.dimension
      .getEntities({
        location: mob.location,
        maxDistance: range,
        excludeFamilies: ["monster", "dev", "util"],
      })
      .filter((entity) => entity !== mob);
    entities.forEach((entity) => {
      if (!entity.isInWater || !entity) {
        return;
      }
      const mouthLocation = Vec3Add(mob.location, Vec3Scale(mob.getViewDirection(), 0.5));
      const vectorToMob = Vec3Subtract(mouthLocation, entity.location);
      const distanceToMob = Vec3Magnitude(vectorToMob);
      if (distanceToMob < 0.5) {
        return;
      }
      const impulseVector = Vec3Scale(Vec3Normalize(vectorToMob), speed);
      try {
        if (entity.typeId === "minecraft:player") {
          applyImpulse(entity, impulseVector);
        } else {
          entity.applyImpulse(impulseVector);
        }
      } catch (e) {
        console.warn(e);
      }
    });
  }, 1);
  system.runTimeout(() => {
    system.clearRun(pullInterval);
    const entities = mob.dimension
      .getEntities({
        location: mob.location,
        maxDistance: 5,
        excludeFamilies: ["monster", "dev", "util"],
      })
      .filter((entity) => entity !== mob);
    entities.forEach((entity) => {
      if (!entity.isInWater || !entity) {
        return;
      }
      const lookDirection = mob.getViewDirection();
      const impulseVector = Vec3Scale(Vec3Normalize(lookDirection), 3);
      try {
        if (entity.typeId === "minecraft:player") {
          applyImpulse(entity, impulseVector);
        } else {
          entity.applyImpulse(impulseVector);
        }
      } catch (e) {
        console.warn(e);
      }
    });
  }, duration * 20);
}
export function teleportToEntityInView(player, item) {
  const entities = player.getEntitiesFromViewDirection({
    maxDistance: 128,
    excludeFamilies: ["dev", "util", "static"],
  });
  if (entities.length === 0) {
    return;
  }
  /* ------------------------------------------------------------------------------------ */
  const cooldown = item.getComponent("minecraft:cooldown");
  if (cooldown.getCooldownTicksRemaining(player) > 0) {
    player.onScreenDisplay.setActionBar(
      `§b[!]§r §3Ability on cooldown! (${cooldown.getCooldownTicksRemaining(player) / 20}s)§r`
    );
    return;
  }
  /* ------------------------------------------------------------------------------------ */
  const closestEntity = entities.reduce((closest, current) => {
    return current.distance < closest.distance ? current : closest;
  }, entities[0]).entity;
  const entityDirection = closestEntity.getViewDirection();
  const entityRotation = Vec3Normalize({ x: entityDirection.x, y: 0, z: entityDirection.z });
  player.playSound("bps_nc.mob.creature_4.float");
  player.playAnimation("animation.bps_nc.creature_4.ability_teleportation");
  player.camera.fade({
    fadeColor: { red: 0, green: 0, blue: 0 },
    fadeTime: { fadeInTime: 0.75, holdTime: 0.0, fadeOutTime: 0.75 },
  });
  closestEntity.addEffect("darkness", 5);
  system.runTimeout(() => {
    const is_teleported = player.tryTeleport(Vec3Add(closestEntity.location, Vec3Scale(entityRotation, 2)), {
      facingLocation: closestEntity.location,
    });
    if (is_teleported) {
      const cooldown = item.getComponent("minecraft:cooldown");
      cooldown.startCooldown(player);
      player.dimension.playSound("bps_nc.mob.creature_4.teleport", player.location);
      player.dimension.playSound("bps_nc.mob.creature_4.teleport", closestEntity.location);
    }
  }, 20 * 0.75);
}
function activateGliding(player, thrustFactor = 0.04, dragFactor = 0.003) {
  player.setDynamicProperty("bps_nc:gliding", true);
  player.playAnimation("animation.bps_nc.shadow_raptor_wings.flight", {
    blendOutTime: 0.2,
    controller: "bps_nc.rotation_controller",
    nextState: "default",
    stopExpression: "q.is_on_ground || q.is_in_water",
  });

  let hSpeed = 0;
  let vSpeed = 0;
  let glideVector = { x: 0, y: 0, z: 0 };

  const interval = system.runInterval(() => {
    const chest = player.getComponent("minecraft:equippable").getEquipment("Chest");
    if (!chest || chest.typeId !== "bps_nc:shadow_raptor_wings" || player.isInWater || player.isOnGround) {
      system.clearRun(interval);
      player.setDynamicProperty("bps_nc:gliding", undefined);
      return;
    }

    // -----------------------------------------------------------------------------------
    const viewDirection = player.getViewDirection();
    // -----------------------------------------------------------------------------------

    hSpeed += -viewDirection.y * thrustFactor - dragFactor;
    hSpeed = clamp(hSpeed, 0, 1000);

    vSpeed += -viewDirection.y * thrustFactor * 0.5;
    vSpeed = clamp(vSpeed, 0, 1000);

    glideVector.x = viewDirection.x * hSpeed;
    glideVector.z = viewDirection.z * hSpeed;
    glideVector.y = viewDirection.y * vSpeed;

    if (Vec3Magnitude(glideVector) <= 0.1) {
      return;
    }
    applyImpulse(player, glideVector);
  }, 1);
}
function burrowUnderground(player) {
  let burrowTime = 0;
  const initBlockUnderPlayer = player.dimension.getBlock(player.location).below().typeId;

  if (initBlockUnderPlayer !== "minecraft:sand" || player.getDynamicProperty("bps_nc:burrowing")) {
    return;
  }
  burrow();

  const interval = system.runInterval(() => {
    const block0 = player.dimension.getBlock(player.location).below();
    const block1 = block0.south();
    const block2 = block0.west();
    const block3 = block0.north();
    const block4 = block0.east();
    const block5 = block1.east();
    const block6 = block1.west();
    const block7 = block3.east();
    const block8 = block3.west();
    const blocksUnderPlayer = [block0, block1, block2, block3, block4, block5, block6, block7, block8];
    const boots = player.getComponent("minecraft:equippable").getEquipment("Feet");
    burrowTime += 1;
    if (burrowTime % 10 === 0) {
      player.dimension.spawnParticle("bps_nc:dune_walker_boots_trail", player.location);
    }
    if (
      !boots ||
      boots.typeId !== "bps_nc:dune_walker_boots" ||
      !blocksUnderPlayer.some((block) => block.typeId === "minecraft:sand")
    ) {
      system.clearRun(interval);
      unburrow();
    }
  }, 1);

  function burrow() {
    player.dimension.spawnParticle("bps_nc:dune_walker_boots_dust", player.location);
    player.playAnimation("animation.bps_nc.dune_walker_boots.burrow", {
      stopExpression: "false",
      nextState: "none",
      blendOutTime: 0,
      controller: "bps_nc.burrow_controller",
      loop: false,
    });
    system.runTimeout(() => {
      player.addEffect("invisibility", 10000000, { showParticles: false });
      player.setDynamicProperty("bps_nc:burrowing", true);
    }, 20);
  }
  function unburrow() {
    player.dimension.spawnParticle("bps_nc:dune_walker_boots_dust", player.location);
    player.setDynamicProperty("bps_nc:burrowing", undefined);
    player.removeEffect("invisibility");
    player.playAnimation("animation.bps_nc.dune_walker_boots.unburrow", {
      stopExpression: "false",
      nextState: "none",
      blendOutTime: 0,
      controller: "bps_nc.burrow_controller",
      loop: false,
    });
  }
}

function morphIntoHorror(player, mob) {
  function removeNamespace(str) {
    // Use regular expression to remove everything before and including the colon
    return str.replace(/^.*?:/, "");
  }
  const availableTypes = ["minecraft:pig", "minecraft:sheep", "minecraft:chicken", "minecraft:cow"];
  if (!availableTypes.includes(mob.typeId)) {
    return;
  }
  player.runCommand("clear @s bps_nc:noface_pearl 0 1");
  const horrorMob = mob.dimension.spawnEntity(`bps_nc:horror_${removeNamespace(mob.typeId)}`, mob.location);
  mob.remove();
  horrorMob.triggerEvent("bps:morph_into");
  system.runTimeout(() => {
    const horrorRide = horrorMob.getComponent("rideable");
    horrorRide.addRider(player);
    player.addEffect("invisibility", 10000000, { showParticles: false });
    const interval = system.runInterval(() => {
      if (horrorRide.getRiders().length <= 0 || !horrorRide.isValid()) {
        system.clearRun(interval);
        player.removeEffect("invisibility");
        horrorMob?.remove();
        player.dimension.spawnEntity(mob.typeId, player.location);
      }
    }, 1);
  }, 2);
}

/* ------------------------------------------------------------------------------------ */
const GAMELOOP = system.runInterval(() => {
  world.getAllPlayers().forEach((player) => {
    const chest = player.getComponent("minecraft:equippable").getEquipment("Chest");
    if (chest && chest.typeId === "bps_nc:stalker_armor") {
      const entities = player.dimension
        .getEntities({
          location: player.location,
          maxDistance: 1,
          excludeFamilies: ["dev", "util", "static"],
        })
        .filter((entity) => entity !== player);
      entities.forEach((entity) => {
        entity.applyDamage(0.3, { cause: "entityAttack", damagingEntity: player });
      });
    }
  });
}, 1);
