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

const overworld = world.getDimension("overworld");

// Retorna de forma segura la ubicación de una entidad.
function getEntityLocation(entity) {
  try {
    return entity?.location || null;
  } catch (e) {
    return null;
  }
}

// Mueve suavemente (interpolación lineal) una entidad (para el UFO)
// hasta targetPos durante durationInTicks.
function moveEntitySmoothly(entity, targetPos, durationInTicks) {
  const startPos = getEntityLocation(entity);
  if (!entity || !startPos) return;
  let tick = 0;
  const intervalId = system.runInterval(() => {
    tick++;
    if (tick >= durationInTicks) {
      system.clearRun(intervalId);
      return;
    }
    const progress = tick / durationInTicks;
    const newPos = {
      x: startPos.x + (targetPos.x - startPos.x) * progress,
      y: startPos.y + (targetPos.y - startPos.y) * progress,
      z: startPos.z + (targetPos.z - startPos.z) * progress,
    };
    try {
      entity.teleport(newPos);
    } catch (e) {
      try {
        overworld.runCommand(
          `tp @e[uuid=${entity.id}] ${newPos.x.toFixed(2)} ${newPos.y.toFixed(2)} ${newPos.z.toFixed(2)}`
        );
      } catch (e2) {
        system.clearRun(intervalId);
      }
    }
  }, 1);
}

// Retorna una posición destino aleatoria cerca del jugador y eleva la Y en 10 bloques.
function getRandomEventPosition(player, range = 60) {
  const loc = player.location;
  const offsetX = (Math.random() - 0.5) * range;
  const offsetZ = (Math.random() - 0.5) * range;
  return {
    x: loc.x + offsetX,
    y: loc.y + 10,
    z: loc.z + offsetZ,
  };
}

/*
  La función maintainLight enciende la luz en el UFO ejecutando "function sapphire/meteors/light"
  cada tick mientras la bandera lightActive sea true.
  Después de 15 segundos (300 ticks) se desactiva la bandera.
  En el siguiente tick se cancela el intervalo y se consulta la ubicación actual del UFO;
  entonces se ejecuta "light_off" en esa posición usando:
  execute positioned <x> <y> <z> run function sapphire/meteors/light_off.
*/
function maintainLight(entity) {
  let lightActive = true;
  const intervalId = system.runInterval(() => {
    if (!lightActive) {
      system.clearRun(intervalId);
      // Obtener la ubicación actual del UFO.
      const loc = getEntityLocation(entity);
      if (loc) {
        try {
          overworld.runCommand(
            `execute positioned ${loc.x.toFixed(2)} ${loc.y.toFixed(2)} ${loc.z.toFixed(2)} run function sapphire/meteors/light_off`
          );
        } catch (e) {
          console.error("Error executing light_off at UFO location:", e);
        }
      } else {
        try {
          overworld.runCommand("function sapphire/meteors/light_off");
        } catch (e) {
          console.error("Error executing light_off:", e);
        }
      }
      return;
    }
    try {
      entity.runCommandAsync("function sapphire/meteors/light");
    } catch (e) {
      system.clearRun(intervalId);
    }
  }, 1);

  // Después de 15 segundos (300 ticks), desactivamos el encendido.
  system.runTimeout(() => {
    lightActive = false;
  }, 300);
}

class UFOAbductionEvent {
  triggerOnPlayer(player) {
    // Comprueba que sea de noche (timeOfDay > 12000).
    if (world.getTimeOfDay() <= 12000) {
      player.sendMessage("§eThe skies are calm. UFO events only occur at night.");
      return;
    }
    // Ocurre aleatoriamente (aprox. 30% de las noches).
    if (Math.random() > 0.3) {
      player.sendMessage("§eNo UFO event tonight, try again later.");
      return;
    }

    const dimension = overworld;
    const numUFOs = Math.floor(Math.random() * 4) + 1;
    player.sendMessage(`§e${numUFOs} UFO${numUFOs > 1 ? "s have" : " has"} appeared!`);

    const basePos = getRandomEventPosition(player);
    for (let i = 0; i < numUFOs; i++) {
      const offsetX = (Math.random() - 0.5) * 20;
      const offsetZ = (Math.random() - 0.5) * 20;
      const targetPos = {
        x: basePos.x + offsetX,
        y: basePos.y,
        z: basePos.z + offsetZ,
      };
      const ufoSpawnPos = { x: targetPos.x, y: targetPos.y + 50, z: targetPos.z };
      const ufo = dimension.spawnEntity("sapphire_meteors:ufo", ufoSpawnPos);
      if (!ufo) {
        player.sendMessage("§cFailed to spawn UFO entity.");
        continue;
      }
      const fallDuration = Math.floor((7 + Math.random() * 8) * 20);
      moveEntitySmoothly(ufo, targetPos, fallDuration);
      maintainLight(ufo); // La luz se apagará en la posición donde se detuvo el UFO después de 15 segundos.

      // Tras un breve retraso, spawnea entre 1 y 2 aliens debajo del UFO.
      system.runTimeout(() => {
        const ufoLoc = getEntityLocation(ufo);
        if (!ufoLoc) return;
        const baseAlienPos = { x: ufoLoc.x, y: ufoLoc.y - 2, z: ufoLoc.z };
        const numAliens = Math.floor(Math.random() * 2) + 1;
        player.sendMessage(
          `§e${numAliens} alien${numAliens > 1 ? "s have" : " has"} appeared below a UFO!`
        );
        for (let j = 0; j < numAliens; j++) {
          const alienOffsetX = (Math.random() - 0.5) * 6;
          const alienOffsetZ = (Math.random() - 0.5) * 6;
          const alienSpawnPos = {
            x: baseAlienPos.x + alienOffsetX,
            y: baseAlienPos.y,
            z: baseAlienPos.z + alienOffsetZ,
          };
          const alien = dimension.spawnEntity("sapphire_meteors:alien", alienSpawnPos);
          if (alien) {
            // Se le aplica el efecto slow_falling para que caiga lentamente.
            alien.addEffect("slow_falling", 400, { amplifier: 0, showParticles: false });
            alien.dimension.spawnParticle("sapphire_meteors:alien_spawn", alien.location);
          }
        }
      }, fallDuration + 20);
    }
  }
}

// Al amanecer, se eliminan todos los UFOs y aliens.
system.runInterval(() => {
  const time = world.getTimeOfDay();
  if (time <= 12000) { // Día
    overworld
      .runCommandAsync("kill @e[type=sapphire_meteors:ufo]")
      .catch((error) => console.error("Error killing UFOs:", error));
    overworld
      .runCommandAsync("kill @e[type=sapphire_meteors:alien]")
      .catch((error) => console.error("Error killing aliens:", error));
  }
}, 20);

world.afterEvents.itemUse.subscribe((event) => {
  const item = event.itemStack;
  const player = event.source;
  if (item.typeId === "sapphire_meteors:ufo_summoner") {
    new UFOAbductionEvent().triggerOnPlayer(player);
  }
});
