import * as WeatherData from "../../data/weatherData";
import { getRandomInt, Vector } from "../../utils";
import { world, system, Player, EntityDamageCause } from "@minecraft/server";
import { getBiome } from "../biome";

const minecraftGlasses = [
  "minecraft:glass",
  "minecraft:orange_stained_glass",
  "minecraft:black_stained_glass",
  "minecraft:green_stained_glass",
  "minecraft:blue_stained_glass",
  "minecraft:gray_stained_glass",
  "minecraft:light_gray_stained_glass",
  "minecraft:lime_stained_glass",
  "minecraft:red_stained_glass",
  "minecraft:pink_stained_glass",
  "minecraft:magenta_stained_glass",
  "minecraft:cyan_stained_glass",
  "minecraft:purple_stained_glass",
  "minecraft:light_blue_stained_glass",
  "minecraft:white_stained_glass",
  "minecraft:brown_stained_glass",
  "minecraft:yellow_stained_glass",
  "minecraft:orange_stained_glass",
];

let weatherRuntimer;

system.runInterval(() => {
  world
    .getDimension("overworld")
    .getPlayers({ farthest: 1 })
    .find((player) =>
      player.runCommandAsync(`scriptevent minty_ew:pick_random`)
    );
}, 20 * 60 * 24);

system.afterEvents.scriptEventReceive.subscribe(async (event) => {
  if (!event.id.startsWith("minty_ew:")) return;

  const player = event.sourceEntity;

  const weatherType = WeatherData.weatherTypes.find(
    (weather) => weather.identifier === event.id.split(":")[1]
  );
  if (!weatherType) return;

  if (event.id.includes("pick_random")) {
    const biome = await getBiome(world.getAllPlayers()[0]);
    if (!biome) return;

    const weatherBiome =
      WeatherData.biomeWeathers.find((wb) => wb.identifier === biome) ||
      WeatherData.biomeWeathers[0];
    const randomDisaster =
      weatherBiome.events[
        Math.floor(Math.random() * weatherBiome.events.length)
      ];
    player.runCommand(`scriptevent minty_ew:${randomDisaster}`);
    return;
  }

  startWeatherEvent(player, weatherType);
});

function startWeatherEvent(player, weatherType) {
  const duration = getRandomInt(
    weatherType.duration.min,
    weatherType.duration.max
  );
  player.setDynamicProperty("minty_ew:is_in_event", true);

  let time = 0;
  const maxTime = 10;

  weatherRuntimer = system.runInterval(() => {
    handleCameraShake(player, weatherType);
    handleDaylightDamage(player, weatherType);

    if (!weatherType.particles) return;

    const velocity = player.getVelocity();

    if (velocity.x <= 0 && velocity.z <= 0) {
      if (time != maxTime) {
        time++;
        return;
      }
      time = 0;
    }

    weatherType.particles.forEach((particle) => {
      const offset = {
        x: player.location.x + particle.offset.x,
        y: weatherType.identifier.match(/^(cyclone|hurricane|thunderstorm)$/)
          ? 120 + particle.offset.y
          : player.location.y + particle.offset.y,
        z: player.location.z + particle.offset.z,
      };
      player.dimension.spawnParticle(particle.name, offset);
    });
  }, 20);

  if (weatherType.entityRain && weatherType.entityRain.enabled)
    spawnEntityRain(player, weatherType);

  if (weatherType.customMob) spawnCustomMob(player, weatherType);

  if (weatherType.normalRain)
    player.dimension.runCommand(
      `execute at "${player.nameTag}" run weather thunder`
    );

  system.runTimeout(() => endWeatherEvent(player, weatherType), 20 * duration);
}

function handleCameraShake(player, weatherType) {
  if (weatherType.camera.shake) {
    player.dimension.runCommand(
      `camerashake add "${player.nameTag}" ${weatherType.camera.shakeIntensity} ${weatherType.duration} positional`
    );
  }
}

function handleDaylightDamage(player, weatherType) {
  const isDaylight = checkDaylight(player);
  player.setDynamicProperty("minty_ew:is_on_daylight", isDaylight);

  if (isDaylight && weatherType.daylight.dealsDamage) {
    player.applyDamage(weatherType.daylight.damage, {
      cause: EntityDamageCause.lava,
    });
  }
}

function checkDaylight(player) {
  let validBlocks = 0;
  const maxHeightBlocks = 319 - player.location.y;

  for (
    let y = player.location.y + 1;
    y <= player.dimension.heightRange.max - 1;
    y++
  ) {
    const position = Vector(player.location.x, y, player.location.z);
    const block = player.dimension.getBlock(position);

    if (block.isAir || minecraftGlasses.includes(block.typeId)) {
      validBlocks += 1;
    }
    if (validBlocks === maxHeightBlocks) return true;
  }
  return false;
}

function spawnEntityRain(player, weatherType) {
  const entityRainInterval = system.runInterval(() => {
    for (let i = 0; i < weatherType.entityRain.entityAmount; i++) {
      const offset = {
        x:
          player.location.x +
          getRandomInt(
            -weatherType.entityRain.radius,
            weatherType.entityRain.radius
          ),
        y: player.location.y + weatherType.entityRain.offsetY,
        z:
          player.location.z +
          getRandomInt(
            -weatherType.entityRain.radius,
            weatherType.entityRain.radius
          ),
      };
      player.dimension
        .spawnEntity(weatherType.entityRain.identifier, offset)
        .addTag("minty_ew:entity_rain_remove");
    }
  }, 20 * weatherType.entityRain.timer);

  system.runTimeout(() => {
    system.clearRun(entityRainInterval);
    clearEntityRain(player);
  }, 20 * weatherType.duration);
}

function spawnCustomMob(player, weatherType) {
  const customEntity = player.dimension.spawnEntity(
    `minty_ew:${weatherType.customMobType}`,
    {
      x: player.location.x + getRandomInt(-8, 8),
      y: player.location.y,
      z: player.location.z + getRandomInt(-8, 8),
    }
  );

  system.runTimeout(
    () => customEntity.triggerEvent("minty_ew:despawn"),
    20 * weatherType.duration
  );
}

function endWeatherEvent(player, weatherType) {
  system.clearRun(weatherRuntimer);
  player.dimension.runCommandAsync(`camerashake stop "${player.nameTag}"`);
  if (weatherType.normalRain)
    player.dimension.runCommandAsync(
      `execute at "${player.nameTag}" run weather clear`
    );
  player.setDynamicProperty("minty_ew:is_in_event", false);
}

function clearEntityRain(player) {
  player.dimension
    .getEntities({ tags: ["minty_ew:entity_rain_remove"] })
    .forEach((entity) => entity.remove());
}

world.afterEvents.entityHurt.subscribe(handleEntityHurt);
world.afterEvents.playerSpawn.subscribe(handlePlayerSpawn);

function handleEntityHurt(event) {
  if (
    !event.hurtEntity.hasComponent("minecraft:is_charged") ||
    event.damageSource.cause !== EntityDamageCause.suffocation
  )
    return;

  const maxHeightBlocks = 319 - event.hurtEntity.location.y;

  for (
    let y = event.hurtEntity.location.y + 1;
    y <= event.hurtEntity.dimension.heightRange.max - 1;
    y++
  ) {
    const position = Vector(
      event.hurtEntity.location.x,
      y,
      event.hurtEntity.location.z
    );
    const block = event.hurtEntity.dimension.getBlock(position);

    if (block.isAir && block.location.y < maxHeightBlocks) {
      event.hurtEntity.tryTeleport(block.location, { checkForBlocks: true });
      break;
    }
  }
}

function handlePlayerSpawn(event) {
  event.player.runCommand(
    `execute as @s[tag=!minty_ew_book] run give @s minty_ew:guidebook`
  );
  event.player.runCommand(
    `execute as @s[tag=!minty_ew_book] run tag @s add minty_ew_book`
  );
}
