import { world, system } from "@minecraft/server";
import { getDifficulty } from "./dungeonSkeletons.js";

const VALID_MOB_IDS = [
  "dungeon:gray_skeleton", "dungeon:rock_monster", "dungeon:dungeonzombie",
  "dungeon:purple_mummy", "dungeon:gray_zombie", "dungeon:brown_skeleton",
  "minecraft:vindicator", "minecraft:wither_skeleton", "minecraft:piglin", "minecraft:piglin_brute", "minecraft:zombie",
  "dungeon:gray_mummy", "dungeon:gorgona",
  "dungeon:miner_skeleton", "dungeon:purple_skeleton", "dungeon:purple_zombie"
];

const TAG = "otherworldly_expert";
const MAX_DISTANCE_SQ = 2.5 * 2.5;
const MIN_DELTA_Y = 1.95;
const MAX_DELTA_Y = 4;
const JUMP_Y = 0.55;
const JUMP_XZ = 0.4;
const MAX_VERTICAL_VEL = 0.01;

const NATURE_GUARD = ["dungeon:nature_guard","dungeon:bat"];
const RETREAT_DISTANCE_SQ = 4.15 * 4.15;
const ATTACK_DISTANCE_SQ = 2.5 * 2.5;

const DIM_IDS = ["overworld", "nether", "the_end"];
let DIMS = null;

function initDims() {
  try {
    DIMS = [
      world.getDimension("overworld"),
      world.getDimension("nether"),
      world.getDimension("the_end")
    ];
  } catch {
    DIMS = null;
  }
}
system.run(initDims);
system.runInterval(() => { if (!DIMS) initDims(); }, 20);

function ai_control(dimension) {
  const players = dimension.getPlayers();
  const pLen = players.length;
  if (pLen === 0) return;

  const pData = new Array(pLen);
  for (let i = 0; i < pLen; i++) {
    const p = players[i];
    const l = p.location;
    pData[i] = { p, x: l.x, y: l.y, z: l.z };
  }

  for (let mi = 0; mi < VALID_MOB_IDS.length; mi++) {
    const mobs = dimension.getEntities({ type: VALID_MOB_IDS[mi], tags: [TAG] });
    if (mobs.length === 0) continue;

    for (const entity of mobs) {
      const el = entity.location;
      const ex = el.x, ey = el.y, ez = el.z;

      let target = null;
      let bestDistSq = MAX_DISTANCE_SQ;

      for (let i = 0; i < pLen; i++) {
        const pd = pData[i];
        const dx = pd.x - ex;
        const dz = pd.z - ez;
        const deltaY = pd.y - ey;
        const distSq = dx * dx + dz * dz;

        if (distSq <= bestDistSq && deltaY > MIN_DELTA_Y && deltaY < MAX_DELTA_Y) {
          bestDistSq = distSq;
          target = pd;
        }
      }

      if (!target) continue;

      const block = dimension.getBlock({
        x: Math.floor(target.x),
        y: Math.floor(target.y) - 1,
        z: Math.floor(target.z)
      });
      if (!block || block.typeId === "minecraft:air") continue;

      const v = entity.getVelocity();
      if (Math.abs(v.y) >= MAX_VERTICAL_VEL) continue;

      const dx = target.x - ex;
      const dz = target.z - ez;
      const len2 = dx * dx + dz * dz;
      if (len2 === 0) continue;

      const inv = 1 / Math.sqrt(len2);
      entity.applyImpulse({ x: dx * inv * JUMP_XZ, y: JUMP_Y, z: dz * inv * JUMP_XZ });
    }
  }
}

function stone_guard_ai(dimension) {
  const players = dimension.getPlayers();
  const pLen = players.length;
  if (pLen === 0) return;

  for (let pi = 0; pi < pLen; pi++) {
    const player = players[pi];
    const pl = player.location;
    const px = pl.x;
    const pz = pl.z;

    const mobs = dimension.getEntities({
      families:["otherworldly_ia_push"],
      location: pl,
      maxDistance: 2.25
    });
    const mLen = mobs.length;
    if (mLen === 0) continue;

    const mx = new Array(mLen);
    const mz = new Array(mLen);
    for (let i = 0; i < mLen; i++) {
      const l = mobs[i].location;
      mx[i] = l.x;
      mz[i] = l.z;
    }

    const hasNeighbor = new Array(mLen).fill(false);

    for (let i = 0; i < mLen; i++) {
      const ax = mx[i], az = mz[i];
      for (let j = i + 1; j < mLen; j++) {
        const dx = mx[j] - ax;
        const dz = mz[j] - az;
        if (dx * dx + dz * dz < 36) {
          hasNeighbor[i] = true;
          hasNeighbor[j] = true;
        }
      }
    }

    for (let i = 0; i < mLen; i++) {
      const mob = mobs[i];
      const mobx = mx[i];
      const mobz = mz[i];

      if (hasNeighbor[i]) {
        for (let j = 0; j < mLen; j++) {
          if (i === j) continue;
          const dx = mobx - mx[j];
          const dz = mobz - mz[j];
          const d2 = dx * dx + dz * dz;
          if (d2 === 0 || d2 >= 36) continue;
          const inv = 1 / Math.sqrt(d2);
          mob.applyImpulse({ x: dx * inv * 0.2, y: 0.0, z: dz * inv * 0.2 });
        }
      }

      const dxp = px - mobx;
      const dzp = pz - mobz;
      const d2p = dxp * dxp + dzp * dzp;

      if (d2p < RETREAT_DISTANCE_SQ) {
        retreatFromPlayer(mob, pl);
      } else if (d2p < ATTACK_DISTANCE_SQ && !hasNeighbor[i]) {
        attackPlayer(mob, player);
      }

      if (pLen > 2 && d2p < ATTACK_DISTANCE_SQ) {
        attackPlayer(mob, player);
      }
    }
  }
}

function hasLineOfSight(dimension, start, end) {
  const dx = end.x - start.x;
  const dy = end.y - start.y;
  const dz = end.z - start.z;

  const dist2 = dx * dx + dy * dy + dz * dz;
  if (dist2 <= 0.0001) return true;

  const distance = Math.sqrt(dist2);
  const step = 0.3;
  const steps = Math.ceil(distance / step);
  const invSteps = 1 / steps;

  for (let i = 0; i <= steps; i++) {
    const t = i * invSteps;
    const x = start.x + dx * t;
    const y = start.y + dy * t;
    const z = start.z + dz * t;

    const block = dimension.getBlock({
      x: Math.floor(x),
      y: Math.floor(y),
      z: Math.floor(z)
    });

    if (!block || block.typeId !== "minecraft:air") return false;
  }

  return true;
}

export function carnivorous_plant(dimension) {
  const difficulty = getDifficulty();

  let chance = 0.75;
  let radius = 4.2;
  let enableBarrage = false;

  switch (difficulty) {
    case "easy": chance = 0.35; enableBarrage = false; break;
    case "normal": chance = 0.5; enableBarrage = true; break;
    case "hard": chance = 0.75; enableBarrage = true; break;
    case "expert":
      chance = 1.0;
      radius = 6.2;
      enableBarrage = true;
      break;
  }

  const players = dimension.getPlayers();
  if (players.length === 0) return;

  const processed = new Set();

  for (const player of players) {
    const gm = player.getGameMode();
    if (gm !== "Survival" && gm !== "Adventure") continue;

    const pl = player.location;
    const px = pl.x;
    const py = pl.y + 1.6;
    const pz = pl.z;

    const plants = dimension.getEntities({
      families: ["dcarnivorous_plant"],
      location: { x: px, y: py, z: pz },
      maxDistance: 32
    });
    const pCount = plants.length;
    if (pCount === 0) continue;

    let index = 0;

    for (const plant of plants) {
      if (processed.has(plant.id)) continue;
      processed.add(plant.id);

      const loc = plant.location;
      const plantEye = { x: loc.x, y: loc.y + 1.5, z: loc.z };
      const playerEye = { x: px, y: py, z: pz };

      const dy = plantEye.y - playerEye.y;
      if (dy > 5 || dy < -5) continue;

      if (!hasLineOfSight(dimension, plantEye, playerEye)) continue;
      if (Math.random() >= chance) continue;

      plant.playAnimation("animation.dcarnivorous_plant.escape");

      let targetPos;
      if (pCount === 1 || (index === 0 && pCount > 1)) {
        targetPos = { x: px, y: pl.y - 1, z: pz };
      } else {
        const angle = (2 * Math.PI / (pCount - 1)) * index;
        targetPos = {
          x: px + Math.cos(angle) * radius,
          y: pl.y,
          z: pz + Math.sin(angle) * radius
        };
      }

      system.runTimeout(() => {
        try { plant.addEffect("minecraft:invisibility", 20, { showParticles: false }); } catch {}
      }, 28);

      system.runTimeout(() => {
        try {
          plant.teleport(targetPos, { dimension, rotation: { x: 0, y: 0 } });
          plant.playAnimation("animation.dcarnivorous_plant.spawn");
          if (enableBarrage) startAttackBarrage(plant, player);
        } catch {}
      }, 32);

      index++;
    }
  }
}

function startAttackBarrage(plant, player) {
  let elapsed = 0;
  const max = 5 * 20;

  const handle = system.runInterval(() => {
    elapsed += 20;

    try {
      const pl = player.location;
      const al = plant.location;
      const dx = pl.x - al.x;
      const dz = pl.z - al.z;
      if (dx * dx + dz * dz < 25) {
        player.addEffect("minecraft:poison", 120, { amplifier: 255, showParticles: true });
      }
    } catch {}

    if (elapsed >= max) system.clearRun(handle);
  }, 20);
}

function retreatFromPlayer(mob, playerPos) {
  const ml = mob.location;
  const dx = ml.x - playerPos.x;
  const dz = ml.z - playerPos.z;
  const d2 = dx * dx + dz * dz;
  if (d2 === 0) return;
  const inv = 1 / Math.sqrt(d2);
  mob.applyImpulse({ x: dx * inv * 0.4, y: 0, z: dz * inv * 0.4 });
}

function attackPlayer(mob, player) {
  const ml = mob.location;
  const pl = player.location;
  const dx = pl.x - ml.x;
  const dz = pl.z - ml.z;
  const d2 = dx * dx + dz * dz;
  if (d2 === 0) return;
  const inv = 1 / Math.sqrt(d2);
  mob.applyImpulse({ x: dx * inv * 0.8, y: 0, z: dz * inv * 0.8 });
}

system.runInterval(() => {
  if (!DIMS) return;
  for (let i = 0; i < DIMS.length; i++) {
    const dimension = DIMS[i];
    ai_control(dimension);
    stone_guard_ai(dimension);
  }
}, 5);

system.runInterval(() => {
  if (!DIMS) return;
  for (let i = 0; i < DIMS.length; i++) {
    carnivorous_plant(DIMS[i]);
  }
}, 100);
