import { world, system, EntityComponentTypes } from '@minecraft/server';

const PROP_STRAFE_DIR = "ai_strafe_direction";
const PROP_STRAFE_TIME = "ai_strafe_timer";
const PROP_ATTACK_RADIUS = "ai_attack_radius";
const PROP_ATTACK_INTERVAL_MIN = "ai_attack_interval_min";
const PROP_ATTACK_INTERVAL_MAX = "ai_attack_interval_max";

const DIMENSIONS = ['overworld', 'nether', 'the_end'];
const ARROW_TYPES = ['minecraft:arrow', 'dungeon:arrow', 'dungeon:stinger_entity'];
const SKELETON_FAMILY = 'skeleton';
const HORNET_FAMILY = 'hornet';

const PUSH_FORCE_CLOSE = 0.02;
const PUSH_FORCE_ALLY = 0.04;
const CHANGE_DIR_CHANCE = 0.4;

let currentDifficulty = 'normal';

export function getDifficulty() {
  const obj = world.scoreboard.getObjective("otherworldly_difficulty");
  const entry = obj ? world.scoreboard.getParticipants().find(p => p.displayName === "difficulty") : null;
  switch (obj?.getScore(entry)) {
    case 0: return "easy";
    case 1: return "normal";
    case 2: return "hard";
    case 3: return "expert";
    default: return "normal";
  }
}

system.runInterval(() => {
  currentDifficulty = getDifficulty();

  for (const dimName of DIMENSIONS) {
    const dimension = world.getDimension(dimName);
    const skeletons = dimension.getEntities({ families: [SKELETON_FAMILY] });
    const hornets = dimension.getEntities({ families: [HORNET_FAMILY] });

    for (const entity of [...skeletons, ...hornets]) {
      try {
        const strafeDir = entity.getDynamicProperty(PROP_STRAFE_DIR);
        let strafeTime = entity.getDynamicProperty(PROP_STRAFE_TIME);

        if (strafeDir === undefined || typeof strafeTime !== "number" || strafeTime < 0) continue;

        try {
          const blockUnder = dimension.getBlock(entity.location);
          if (blockUnder?.typeId.includes("water")) continue;
        } catch (e) { }

        strafeTime--;
        entity.setDynamicProperty(PROP_STRAFE_TIME, strafeTime);
        if (strafeTime < 0) {
          entity.isSneaking = false;
          entity.setDynamicProperty(PROP_STRAFE_DIR, undefined);
          continue;
        }

        let controller = entity;
        try {
          const ridingComp = entity.getComponent(EntityComponentTypes.Riding);
          controller = ridingComp?.entityRidingOn ?? entity;
        } catch (e) { }

        try {
          const typeFam = controller.getComponent(EntityComponentTypes.TypeFamily);
          if (!controller.isOnGround && !typeFam?.hasTypeFamily(HORNET_FAMILY) && typeFam?.hasTypeFamily("inanimate")) continue;
        } catch (e) { }

        const attackRange = entity.getDynamicProperty(PROP_ATTACK_RADIUS) ?? 20;
        let avoidClose = 1;

        try {
          const targets = entity.getEntitiesFromViewDirection({ excludeTypes: ARROW_TYPES, maxDistance: attackRange });
          for (const target of targets) {
            if (target.entity.id !== entity.id && target.entity.id !== controller.id && target.distance < attackRange * 0.5) {
              avoidClose = -1;
              break;
            }
          }
        } catch (e) { }

        if (currentDifficulty === "expert") {
          try {
            const closeEntities = dimension.getEntities({ location: entity.location, maxDistance: 8 });
            for (const e of closeEntities) {
              if (e.id !== entity.id && !ARROW_TYPES.includes(e.typeId)) {
                const dx = e.location.x - entity.location.x;
                const dz = e.location.z - entity.location.z;
                const magSq = dx * dx + dz * dz;
                if (magSq > 0 && magSq < 36) {
                  const mag = Math.sqrt(magSq);
                  entity.applyImpulse({
                    x: -dx / mag * PUSH_FORCE_CLOSE,
                    y: 0,
                    z: -dz / mag * PUSH_FORCE_CLOSE
                  });
                }
              }
            }
          } catch (e) { }
        }

        try {
          const yaw = controller.getRotation().y;
          const angle = (yaw + strafeDir * (90 - avoidClose * 30) + 145) * Math.PI / 180;
          const moveComp = controller.getComponent("minecraft:movement");
          const baseSpeed = moveComp?.currentValue ?? 0.15;
          const speed = baseSpeed * (controller.id !== entity.id ? 0.25 : 0.15);

          let impulseY = 0;
          const fam = controller.getComponent(EntityComponentTypes.TypeFamily);
          if (fam?.hasTypeFamily(HORNET_FAMILY)) {
            impulseY = (Math.random() - 0.5) * 0.05;
          }

          controller.applyImpulse({
            x: Math.cos(angle) * speed,
            y: impulseY,
            z: Math.sin(angle) * speed
          });
        } catch (e) { }

        if (currentDifficulty === "hard" || currentDifficulty === "expert") {
          try {
            const nearby = dimension.getEntities({ families: [SKELETON_FAMILY, HORNET_FAMILY], location: entity.location, maxDistance: 12 });
            for (const ally of nearby) {
              if (ally.id !== entity.id) {
                const dx = ally.location.x - entity.location.x;
                const dz = ally.location.z - entity.location.z;
                const magSq = dx * dx + dz * dz;
                if (magSq < 16 && magSq > 0.0001) {
                  const mag = Math.sqrt(magSq);
                  entity.applyImpulse({
                    x: -dx / mag * PUSH_FORCE_ALLY,
                    y: 0,
                    z: -dz / mag * PUSH_FORCE_ALLY
                  });
                }
              }
            }
          } catch (e) { }
        }
      } catch (e) { }
    }
  }
});

world.afterEvents.entitySpawn.subscribe(({ entity: arrow }) => {
  if (!ARROW_TYPES.includes(arrow.typeId)) return;

  const shooter = arrow.getComponent("minecraft:projectile")?.owner;
  const shooterFamily = shooter?.getComponent("minecraft:type_family");
  if (!shooterFamily?.hasTypeFamily(SKELETON_FAMILY) && !shooterFamily?.hasTypeFamily(HORNET_FAMILY)) return;

  if (shooter.isSneaking) {
    arrow.remove();
    shooter.isSneaking = false;
    return;
  }

  const headLoc = shooter.getHeadLocation();
  let velocity = arrow.getVelocity();
  if (!velocity || !isFinite(velocity.x) || !isFinite(velocity.y) || !isFinite(velocity.z)) velocity = shooter.getViewDirection();

  const targets = shooter.getEntitiesFromViewDirection({ maxDistance: 24 }).filter(e => e.entity.id !== shooter.id && e.entity.id !== arrow.id);
  const target = targets.length > 0 ? targets[0].entity : null;

  if (target) {
    const future = { ...target.location };
    const vel = target.getVelocity?.();
    if (vel) {
      future.x += vel.x * 0.6;
      future.z += vel.z * 0.6;
    }
    const dx = future.x - headLoc.x;
    const dz = future.z - headLoc.z;
    const distSq = dx * dx + dz * dz;
    if (distSq > 0.0001) {
      const dist = Math.sqrt(distSq);
      velocity.x += dx / dist * 0.2;
      velocity.z += dz / dist * 0.2;
    }
  }

  arrow.tryTeleport({ x: headLoc.x + velocity.x, y: headLoc.y + velocity.y, z: headLoc.z + velocity.z });
  arrow.applyImpulse(velocity);

  const difficulty = currentDifficulty;
  const range = shooter.getDynamicProperty(PROP_ATTACK_RADIUS) ?? 20;
  let minDelay = 0;
  const targetsInRange = shooter.getEntitiesFromViewDirection({ excludeTypes: ARROW_TYPES });
  for (const t of targetsInRange) {
    const rider = shooter.getComponent(EntityComponentTypes.Riding)?.entityRidingOn ?? shooter;
    if (t.entity.id !== arrow.id && t.entity.id !== shooter.id && t.entity.id !== rider.id) {
      minDelay = Math.min(t.distance, range);
      break;
    }
  }

  let scale = 1;
  switch (difficulty) {
    case 'easy': scale = 1; break;
    case 'normal': scale = 0.75; break;
    case 'hard': scale = 0.45; break;
    case 'expert': scale = 0.15; break;
  }

  const intervalMin = (shooter.getDynamicProperty(PROP_ATTACK_INTERVAL_MIN) ?? 1.0) * 20 * scale;
  const intervalMax = (shooter.getDynamicProperty(PROP_ATTACK_INTERVAL_MAX) ?? 2.5) * 20 * scale;
  const delay = intervalMin !== intervalMax ? Math.floor(intervalMin + (minDelay / range) * (intervalMax - intervalMin)) : intervalMax;

  system.runTimeout(() => {
    if (shooter?.getComponent("minecraft:health")?.currentValue > 0) shooter.isSneaking = false;
  }, Math.max(5, delay - 25));

  shooter.setDynamicProperty(PROP_STRAFE_TIME, delay + 30);
  const dir = shooter.getDynamicProperty(PROP_STRAFE_DIR);
  shooter.setDynamicProperty(PROP_STRAFE_DIR, dir === undefined ? (Math.random() > 0.5 ? 1 : -1) : (Math.random() < CHANGE_DIR_CHANCE ? -dir : dir));
});
