import { GameMode, ItemStack, StructureSaveMode, system, world, EntityComponentTypes } from "@minecraft/server";
import { getPlayersNearEntity } from "./banned_wither";
import { getVibrantVisuals } from "../biome/ambient_handler";

const ARENA_EXTENDED_PROPERTY = "hb_hr:banned_wither_arena_extended";
const ARENA_PAUSED_PROPERTY = "hb_hr:banned_wither_arena_paused";

const ARENA_PAUSED_COOLDOWN = 60; // how long it takes for arena to despawn if all players are away - in ms

const SPAWNING_CUTSCENE_TAG = "hb_hr_banned_wither_spawn_cutscene";
const ON_ARENA_TAG = "hb_hr_on_banned_wither_arena";

const PAUSED_MARKERS_PROPERTY = "hb_hr:banned_wither_paused_markers";

const ALLOW_BLOCKS = ["hb_hr:banned_wither_skull_b1_block", "hb_hr:banned_wither_skull_b2_block", "hb_hr:banned_wither_skull_b3_block"];

export function banned_wither_marker() {
  onReload();

  // cancel block breaking on arena
  world.beforeEvents.playerBreakBlock.subscribe((event) => {
    const player = event?.player;
    if (player.getGameMode() === GameMode.creative) return;

    if (player?.hasTag(ON_ARENA_TAG)) {
      const block = event.block;

      if (!ALLOW_BLOCKS.includes(block.typeId)) {
        const location = event.block?.center();
        system.runTimeout(() => {
          player?.dimension?.spawnParticle("minecraft:basic_smoke_particle", {
            x: location.x,
            y: location.y + 0.5,
            z: location.z,
          });
        }, 1);

        event.cancel = true;
      }
    }
  });

  // cancel block placing & interacting with chests etc. on arena to prevent duplication using structures
  world.beforeEvents.playerInteractWithBlock.subscribe((event) => {
    const player = event?.player;
    if (player.getGameMode() === GameMode.creative) return;

    if (player?.hasTag(ON_ARENA_TAG)) {
      const item = event?.itemStack;
      const block = event?.block;

      if (block.typeId !== "hb_hr:banned_wither_block_head") {
        event.cancel = true;

        if (!item) return;
        const location = event.block?.center();
        system.runTimeout(() => {
          player?.dimension?.spawnParticle("minecraft:basic_smoke_particle", {
            x: location.x,
            y: location.y + 0.5,
            z: location.z,
          });
        }, 1);

        return;
      }
      if (!item || !ALLOW_BLOCKS.includes(item.typeId)) {
        event.cancel = true;
      }
    }
  });

  // cancel explosion that breaks blocks on arena
  world.beforeEvents.explosion.subscribe((event) => {
    const blocks = event?.getImpactedBlocks();

    if (blocks.length > 0) {
      const block = blocks[0];

      // far distance
      const far_markers = block.dimension.getEntities({
        type: "hb_hr:banned_wither_marker",
        maxDistance: 48,
        location: block.location,
        propertyOptions: [
          {
            propertyId: "hb_hr:fight_status",
            value: { equals: "fight_in_progress" },
          },
        ],
      });
      if (far_markers.length > 0) {
        event.cancel = true;
        return;
      }

      // close distance
      const close_markers = block.dimension.getEntities({
        type: "hb_hr:banned_wither_marker",
        maxDistance: 16,
        location: block.location,
        propertyOptions: [
          {
            propertyId: "hb_hr:fight_status",
            value: { equals: "fight_default" },
          },
        ],
      });
      if (close_markers.length > 0) {
        event.cancel = true;
      }
    }
  });

  world.afterEvents.playerSpawn.subscribe((event) => {
    const player = event.player;
    if (player?.hasTag(SPAWNING_CUTSCENE_TAG)) {
      spawnBossClearEffects(player);
    }

    if (player?.hasTag(ON_ARENA_TAG)) {
      player.runCommandAsync(`/execute unless entity @e[type=hb_hr:banned_wither_marker,r=48] run tag @s remove ${ON_ARENA_TAG}`);
    }
  });

  system.afterEvents.scriptEventReceive.subscribe((event) => {
    const source = event.sourceEntity;
    if (source?.typeId !== "hb_hr:banned_wither_marker") return;

    if (event.id === "hb_hr:banned_wither") {
      if (event?.message === "spawn") {
        spawnBoss(source);
      }
      if (event?.message === "despawn") {
        despawnBoss(source);
      }
      if (event?.message === "kill") {
        killBoss(source);
      }
      if (event?.message === "pause_check") {
        pauseCheck(source);
      }
      if (event?.message === "levitation") {
        levitation(source);
      }
      if (event?.message === "particles") {
        triggerArenaParticles(source);
      }
    }
  });

  system.runInterval(() => {
    const paused_markers_data = world.getDynamicProperty(PAUSED_MARKERS_PROPERTY) ?? "{}";
    const paused_markers = JSON.parse(paused_markers_data);

    if (Object.keys(paused_markers).length === 0) return;

    for (const [id, marker] of Object.entries(paused_markers)) {
      const time = marker.time;

      // Decrease time
      if (time > 0) {
        marker.time--;

        // message players that left arena:
        if (marker.players?.length > 0) {
          const players = world.getAllPlayers();
          const message = { text: `You left the arena. Enter the arena in the next §c§l${time - 1}§r§f seconds to continue the fight!` };

          for (let player of players) {
            if (marker.players.includes(player.id)) {
              player.onScreenDisplay.setActionBar(message);
            }
          }
        }
      }
    }

    // save updated data
    world.setDynamicProperty(PAUSED_MARKERS_PROPERTY, JSON.stringify(paused_markers));
  }, 20);
}

function levitation(marker) {
  const y = marker.location.y;
  const arena_players = marker.dimension.getEntities({ type: "minecraft:player", maxDistance: 30, location: marker.location });

  if (arena_players.length <= 0) return;
  for (let player of arena_players) {
    if (player.location.y < y) {
      player?.addEffect("minecraft:levitation", 1 * 20, { amplifier: 2, showparticles: false });
    }
  }
}

function pauseCheck(marker) {
  const paused_markers_data = world.getDynamicProperty(PAUSED_MARKERS_PROPERTY) ?? "{}";
  const paused_markers = JSON.parse(paused_markers_data);
  const id = marker.id;

  const arena_players_count = marker.dimension.getEntities({ type: "minecraft:player", maxDistance: 28, location: marker.location }).length;

  // check if bossfight is paused
  if (paused_markers[id]) {
    // check if bossfight should end
    const time = paused_markers[marker.id]["time"];
    if (time <= 0) {
      // end bossfight

      if (marker.getProperty("hb_hr:fight_status") === "fight_in_progress") despawnBoss(marker);

      delete paused_markers[marker.id];
      world.setDynamicProperty(PAUSED_MARKERS_PROPERTY, JSON.stringify(paused_markers));
      return;
    }

    // check if bossfight should be unpaused
    if (arena_players_count > 0) {
      delete paused_markers[id];
      world.setDynamicProperty(PAUSED_MARKERS_PROPERTY, JSON.stringify(paused_markers));
      return;
    }
  } else {
    // check if bossfight should be paused
    if (arena_players_count <= 0) {
      const close_players = marker.dimension.getEntities({ type: "minecraft:player", maxDistance: 48, location: marker.location });
      paused_markers[id] = { time: ARENA_PAUSED_COOLDOWN, players: [] };

      for (const player of close_players) {
        paused_markers[id].players.push(player.id);
      }

      world.setDynamicProperty(PAUSED_MARKERS_PROPERTY, JSON.stringify(paused_markers));
    }
  }
}

function spawnBossClearEffects(player) {
  player.removeTag(SPAWNING_CUTSCENE_TAG);
  player.runCommand("/inputpermission set @s movement enabled");
  player.runCommand("/inputpermission set @s camera enabled");
  player.runCommand("/camera @s clear");
  player.runCommand("/effect @s resistance 0 0");
}

function spawnBoss(marker) {
  const isArenaExtended = marker.getDynamicProperty(ARENA_EXTENDED_PROPERTY) ?? false;
  const dimension = marker.dimension;
  const location = marker.location;

  // Save terrain near arena before loading extended arena structure
  if (!isArenaExtended) {
    saveTerrainStructure(marker);
    marker.setDynamicProperty(ARENA_EXTENDED_PROPERTY, true);
  }
  // Load arena
  loadExtendedArena(marker, 9);

  // Tag players for cutscene
  marker.runCommand(`/tag @a remove ${SPAWNING_CUTSCENE_TAG}`);

  const players = dimension.getEntities({
    type: "minecraft:player",
    location: location,
    maxDistance: 32,
  });

  if (players.length === 0) return;
  let i = 5;
  for (let player of players) {
    player.addTag(SPAWNING_CUTSCENE_TAG);

    // player positions
    if (i == 0) {
      player.runCommand(`/execute at @e[type=hb_hr:banned_wither_marker,c=1,r=48] positioned ~ ~0.5 ~-9 run tp @s ~ ~ ~ 0 -22`);
      marker.runCommand(`/setblock ~ ~ ~-9 minecraft:soul_soil`);
    } else if (i == 1) {
      player.runCommand(`/execute at @e[type=hb_hr:banned_wither_marker,c=1,r=48] positioned ~-1.5 ~0.5 ~-9 run tp @s ~ ~ ~ -10 -22`);
      marker.runCommand(`/setblock ~-1.5 ~ ~-9 minecraft:soul_soil`);
    } else if (i == 2) {
      player.runCommand(`/execute at @e[type=hb_hr:banned_wither_marker,c=1,r=48] positioned ~1.5 ~0.5 ~-9 run tp @s ~ ~ ~ 10 -22`);
      marker.runCommand(`/setblock ~-1.5 ~ ~-9 minecraft:soul_soil`);
    } else {
      player.runCommand(`/execute at @e[type=hb_hr:banned_wither_marker,c=1,r=48] positioned ~ ~0.5 ~-11 run tp @s ~ ~ ~ 0 -22`);
      marker.runCommand(`/setblock ~ ~ ~-11 minecraft:soul_soil`);
    }

    i += 1;
  }

  // CUTSCENE
  // remove items from arena
  marker.runCommand("/tp @e[type=minecraft:item,r=32] ~~~32");
  marker.runCommand("/tp @e[type=minecraft:xp_orb,r=32] ~~~32");
  marker.runCommand("/tp @e[family=fish,r=32] ~~~32");
  marker.runCommand("/tp @e[family=mob,r=32] ~~~32");

  // spawn boss
  marker.triggerEvent("fight_in_progress");
  marker.runCommand(`/execute unless entity @e[type=hb_hr:banned_wither,r=48] run summon hb_hr:banned_wither ~ ~1 ~ 180 0 hb_hr:phase_0`);
  marker.runCommand(`/execute at @s run tp @e[type=hb_hr:banned_wither,c=1,r=48] ~ ~1 ~ 180 0`);

  // effects
  marker.runCommand(`/inputpermission set @a[tag=${SPAWNING_CUTSCENE_TAG}] movement disabled`);
  marker.runCommand(`/inputpermission set @a[tag=${SPAWNING_CUTSCENE_TAG}] camera disabled`);
  marker.runCommand(`/effect @a[tag=${SPAWNING_CUTSCENE_TAG}] resistance 128 100 true`);

  // camera
  marker.runCommand(`/execute as @a[tag=${SPAWNING_CUTSCENE_TAG}] at @s run camera @s set minecraft:free pos ^-1 ^1.5 ^0.3 facing ${location.x} ${location.y + 7} ${location.z}`);
  marker.runCommand(`/execute as @a[tag=${SPAWNING_CUTSCENE_TAG}] at @s run camera @s set minecraft:free ease 9.3 in_sine pos ^-1.2 ^2 ^-3 facing ${location.x} ${location.y + 7} ${location.z}`);

  // wither self building
  system.runTimeout(() => {
    marker.runCommand(`/camerashake add @a[tag=${SPAWNING_CUTSCENE_TAG}] 0.2 0.3 positional`);
  }, 5.2 * 20);
  system.runTimeout(() => {
    marker.runCommand(`/camerashake add @a[tag=${SPAWNING_CUTSCENE_TAG}] 0.2 0.3 positional`);
  }, 6.5 * 20);

  system.runTimeout(() => {
    marker.runCommand(`/camerashake add @a[tag=${SPAWNING_CUTSCENE_TAG}] 0.2 0.5 positional`);
  }, 7.4 * 20);
  system.runTimeout(() => {
    marker.runCommand(`/camerashake add @a[tag=${SPAWNING_CUTSCENE_TAG}] 0.2 0.5 positional`);
  }, 7.8 * 20);

  system.runTimeout(() => {
    marker.runCommand(`/camerashake add @a[tag=${SPAWNING_CUTSCENE_TAG}] 0.2 0.7 positional`);
  }, 8.5 * 20);

  // scream
  system.runTimeout(() => {
    marker.runCommand(`/execute as @a[tag=${SPAWNING_CUTSCENE_TAG}] at @s run camera @s clear`);
  }, 9.2 * 20);

  system.runTimeout(() => {
    marker.runCommand(`/camerashake add @a[tag=${SPAWNING_CUTSCENE_TAG}] 0.1 1.5 positional`);
    marker.runCommand(`/camerashake add @a[tag=${SPAWNING_CUTSCENE_TAG}] 0.1 1.5 rotational`);
  }, 9.6 * 20);

  const fade_cooldown = 10;
  // system.runTimeout(() => {
  // 	players.forEach(player => {
  // 		if (player.isValid()) {
  // 			player.camera.fade({
  // 				"fadeTime": {
  // 					"fadeInTime": 1,
  // 					"holdTime": 1,
  // 					"fadeOutTime": 1
  // 				}
  // 			})
  // 		}
  // 	}
  // 	);
  // }, fade_cooldown * 20);

  system.runTimeout(
    () => {
      players.forEach((player) => {
        if (player.isValid()) {
          spawnBossClearEffects(player);
        }
      });
    },
    (fade_cooldown + 1.5) * 20,
  );
}

function saveTerrainStructure(marker) {
  const structure_id = `hb_hr:${marker.id}`;
  const location = marker.location;

  const structures = world.structureManager.getWorldStructureIds();
  if (structures.includes(structure_id)) {
    world.structureManager.delete(structure_id);
  }

  const startingPos = {
    x: location.x - 28,
    y: location.y - 27,
    z: location.z - 28,
  };
  const endingPos = {
    x: location.x + 28,
    y: location.y + 54,
    z: location.z + 28,
  };

  // Save area
  world.structureManager.createFromWorld(structure_id, marker.dimension, startingPos, endingPos, {
    includeEntities: false,
    saveMode: StructureSaveMode.World,
  });
}
function loadTerrainStructure(marker) {
  const structure_id = `hb_hr:${marker.id}`;
  const location = marker.location;

  const startingPos = {
    x: location.x - 28,
    y: location.y - 9 - 18,
    z: location.z - 28,
  };

  world.structureManager.place(structure_id, marker.dimension, startingPos);

  // place wither spawning structure
  marker.triggerEvent("hb_hr:place_structure");
  // // console.warn(`${location.x} ${location.y} ${location.z}`)
  // Teleport player to save place when restoring spawn area
  marker.runCommand(`teleport @a[r=40] ${location.x} ${location.y + 1.5} ${location.z - 1}`);
}

function killBoss(marker) {
  marker.triggerEvent("fight_default");
  loadTerrainStructure(marker);
  spawnLootItems(marker);
  marker.setDynamicProperty(ARENA_EXTENDED_PROPERTY, false);
  marker.runCommand("/event entity @e[family=hb_hr:banned_wither,family=projectile,r=48] hb_hr:despawn");
}

function despawnBoss(marker) {
  marker.triggerEvent("fight_default");
  loadTerrainStructure(marker);
  marker.setDynamicProperty(ARENA_EXTENDED_PROPERTY, false);

  // drop skulls
  const location = marker.location;
  const item_location = {
    x: location.x,
    y: location.y + 3,
    z: location.z,
  };

  for (let i = 1; i <= 3; i++) {
    const item = new ItemStack(`hb_hr:banned_wither_skull_b${i}_block`, 1);
    marker.dimension.spawnItem(item, item_location);
  }

  // if boss exists for any reason - despawn it
  marker.runCommand("/event entity @e[type=hb_hr:banned_wither,c=1,r=48] hb_hr:despawn");
  marker.runCommand("/event entity @e[family=hb_hr:banned_wither,family=projectile,r=48] hb_hr:despawn");
}

function loadExtendedArena(marker, structure_load_time) {
  const structure_id = "hb_hr:banned_wither_arena_extended";
  const location = marker.location;

  const startingPos = {
    x: location.x - 28,
    y: location.y - 26,
    z: location.z - 28,
  };

  world.structureManager.place(
    structure_id,
    marker.dimension,
    startingPos,
    // {
    // 	"animationMode": StructureAnimationMode.Layers,
    // 	"animationSeconds": 1
    // }
  );
}

function onReload() {
  const players = world.getAllPlayers();
  for (let player of players) {
    if (player?.hasTag(SPAWNING_CUTSCENE_TAG)) {
      spawnBossClearEffects(player);
    }

    if (player?.hasTag(ON_ARENA_TAG)) {
      player.runCommandAsync(`/execute unless entity @e[type=hb_hr:banned_wither_marker,r=48] run tag @s remove ${ON_ARENA_TAG}`);
    }
  }
}

// spawn particles for each player inside the arena
function triggerArenaParticles(entity) {
  const nearbyPlayers = getPlayersNearEntity(entity, 30);
  if (nearbyPlayers.length == 0) {
    return;
  }
  nearbyPlayers.forEach((player) => {
    const pos = entity.location;

    // NEW: check global Vibrant Visuals DP
    const vibrant = getVibrantVisuals(player);

    const particleName = vibrant ? "hb_hr:vv_arena_main" : "hb_hr:arena_main";

    try {
      player.spawnParticle(particleName, { x: pos.x, y: pos.y, z: pos.z });
    } catch (error) {}
  });
}

function spawnLootItems(marker) {
  const netherStar = new ItemStack("nether_star", 1);
  const cloak = new ItemStack("hb_hr:banned_cloak", 1);
  world.getDimension(marker.dimension.id).spawnItem(netherStar, { x: marker.location.x, y: marker.location.y + 3.5, z: marker.location.z });
  world.getDimension(marker.dimension.id).spawnItem(cloak, { x: marker.location.x, y: marker.location.y + 3.5, z: marker.location.z });
}
