import { world, system, ItemStack } from "@minecraft/server";
import * as utils from "./utils";
import * as Vector3 from "./vector3";
import * as dungeons from "./dungeons";

const PORTAL_DISTANCE = 6;

const portalOffset = {
	"south": {x: 0, y: 0, z: 6},
	"north": {x: 0, y: 0, z: -6},
	"east": {x: 6, y: 0, z: 0},
	"west": {x: -6, y: 0, z: 0}
}

// Convert item id to dungeon id
const dungeonIds = [
	"goe_bos:spider_nest",
	"goe_bos:piglins_lair",
	"goe_bos:haunted_tomb",
	"goe_bos:wardens_chamber",
	"goe_bos:necromancer_ruins",
	"goe_bos:frostbite_cavern",
	"goe_bos:forbidden_crypt",
	"goe_bos:void_sanctum"
]

// Placing portals
export async function onItemUse(event)
{
	const player = event.source;
	const itemId = event.itemStack.typeId;

	if (!dungeonIds.includes(itemId)) return;
	const dungeonId = itemId;
	// utils.debug(dungeonId);

	if (!player.dimension.id.endsWith("overworld"))
	{
		utils.showPlayerErrorMessage(player, "The Dungeon Portal can be summoned only in the Overworld.");
		return;
	}

	if (await (checkFornearbyPortals(player)) === true)
	{
		utils.showPlayerErrorMessage(player, "Can't activate the Dungeon Portal. It's too close to another active portal.");
		return;
	}

	const tpLocation = event.itemStack.getDynamicProperty("goe_bos_tp_location");
	const tpRotation = event.itemStack.getDynamicProperty("goe_bos_tp_rotation");
	await placePortal(player, dungeonId, tpLocation, tpRotation);

	if (player.getGameMode() !== "creative")
		await utils.runPlayerCommand(player, `clear @s ${itemId} 0 1`); // Remove item from inventory
}

async function placePortal(player, dungeonId, tpLocation, tpRotation) {
	// Calculate portal location and rotation
	const playerLocation = Vector3.copy(player.location);
	const playerRotation = Vector3.copy(player.getRotation());
	const playerDirection = Vector3.copy(player.getViewDirection());
	const portalLocation = playerLocation.add(playerDirection.multiply(PORTAL_DISTANCE));
	portalLocation.y = playerLocation.y;

	// Spawn portal
	utils.runPlayerCommand(player, `summon goe_bos:portal ${portalLocation.toCommand()} ~ ${playerRotation.y} ${dungeonId}`);

	// Wait 1 tick for next step
	system.runTimeout(() => finishPortal(player, portalLocation, dungeonId, tpLocation, tpRotation), 1);
}

async function finishPortal(player, portalLocation, dungeonId, tpLocation, tpRotation) {
	const portal = player.dimension.getEntities({
		location: portalLocation,
		type: "goe_bos:portal",
		closest: 1
	})[0];

	if (!portal || !portal.isValid()) return;

	portal.addTag("goe_bos_portal_overworld");
	portal.setDynamicProperty("goe_bos_dungeon_id", dungeonId);

	if (tpLocation && tpRotation) {
		portal.setDynamicProperty("goe_bos_tp_location", tpLocation);
		portal.setDynamicProperty("goe_bos_tp_rotation", tpRotation);
		portal.setDynamicProperty("goe_bos_portal_is_active", true);
		portal.addTag("goe_bos_portal_is_active");
	} else {
		// Load dungeon and set spawn point
		await dungeons.loadDungeon(dungeonId, portal);
	}
	
}

export async function placeExitPortal(dungeonId, portalLocation, portalRotation, tpLocation) {
	// Spawn portal
	utils.runCommand(`summon goe_bos:portal ${portalLocation.x} ${portalLocation.y} ${portalLocation.z} ~ ${portalRotation} goe_bos:back`, utils.the_end);

	// Wait 1 tick for next step
	system.runTimeout(() => finishExitPortal(portalLocation, dungeonId, portalRotation, tpLocation), 1);
}

async function finishExitPortal(portalLocation, dungeonId, portalRotation, tpLocation) {
	const portal = utils.the_end.getEntities({
		location: portalLocation,
		type: "goe_bos:portal",
		closest: 1
	})[0];
	
	if (!portal || !portal.isValid) return;

	// This is used because rotation in summon command doesnt work if player is in another dimension
	portal.teleport(portal.location, { rotation: { x: 0, y: portalRotation } });

	portal.addTag("goe_bos_portal_the_end");
	portal.setDynamicProperty("goe_bos_dungeon_id", dungeonId);
	portal.setDynamicProperty("goe_bos_tp_location", tpLocation);
	portal.setDynamicProperty("goe_bos_portal_is_active", true);
}

export async function checkFornearbyPortals(player)
{
	let nearbyPortals = await utils.getEntitiesInRange("goe_bos:portal", player.dimension, player.location, 0, 10);
	return (nearbyPortals && nearbyPortals.length > 0);
}

export async function onTick()
{
	let portals = await utils.getEntities("goe_bos:portal");
	if (!portals) return;
	for (let portal of portals)
	{
		let nearbyPlayers = await utils.getPlayersInRange(portal.dimension, portal.location, 0, 8);
		for (let nearbyPlayer of nearbyPlayers)
		{
			if (!portal.isValid() || !portal.getDynamicProperty("goe_bos_portal_is_active")) {
				nearbyPlayer.removeTag("goe_bos_teleport_active");
				continue;
			}

			if (nearbyPlayer.dimension.id === "minecraft:the_end")
				nearbyPlayer.onScreenDisplay.setActionBar("§bStep into the portal to exit the dungeon.");
			else if (nearbyPlayer.dimension.id === "minecraft:overworld")
				nearbyPlayer.onScreenDisplay.setActionBar("§bStep into the portal to enter the dungeon.");

			if (Vector3.distance(portal.location, nearbyPlayer.location) > 2)
				continue;

			if (!nearbyPlayer.hasTag("goe_bos_teleport_active"))
			{
				// start teleport
				teleportPlayer(nearbyPlayer, portal);
				// await dungeons.displayExitForm(nearbyPlayer, portal);
				nearbyPlayer.addTag("goe_bos_teleport_active");
			}
		}
			
		let farPlayers = await utils.getPlayersInRange(portal.dimension, portal.location, 2.1, 3);
		for (let farPlayer of farPlayers)
		{
			if (farPlayer.hasTag("goe_bos_teleport_active"))
				farPlayer.removeTag("goe_bos_teleport_active");
		}
	}
}

export async function teleportPlayer(player, portal) 
{
	utils.runPlayerCommand(player, "inputpermission set @s movement disabled");
	utils.runPlayerCommand(player, "inputpermission set @s camera disabled");
	
	let tpIntoDungeon = (portal.dimension.id === "minecraft:the_end") ? false : true;
	if (tpIntoDungeon) {
		const exitDestinationLocation = {
			x: portal.location.x - portal.getViewDirection().x * 5,
			y: portal.location.y,
			z: portal.location.z - portal.getViewDirection().z * 5
		};
		player.setDynamicProperty("goe_bos_tp_back", exitDestinationLocation);
		player.setDynamicProperty("goe_bos_rotation_back", portal.getRotation().y);

		// Check spawn point
		if (player.getSpawnPoint()) {
			player.setDynamicProperty(
				"goe_bos_spawn_point", 
				{ 
					x: player.getSpawnPoint().x,
					y: player.getSpawnPoint().y,
					z: player.getSpawnPoint().z
				}
			);
		}

		player.setSpawnPoint({
			dimension: utils.overworld,
			x: exitDestinationLocation.x,
			y: exitDestinationLocation.y,
			z: exitDestinationLocation.z
		});
	}

	player.dimension.spawnParticle("goe_bos:teleport", player.location);
	utils.playSound(player, "@a", "goe_bos:portal_teleport");

	const dungeonId = portal.getDynamicProperty("goe_bos_dungeon_id");

	if (tpIntoDungeon) {
		player.addTag("goe_bos_in_dungeon");
		player.addTag("spider_nest");
		player.setDynamicProperty("goe_bos_dungeon_id", dungeonId);
	}
	else {
		player.setDynamicProperty("goe_bos_dungeon_id", undefined);
		player.removeTag("goe_bos_in_dungeon");
		player.removeTag("spider_nest");
	}

	// Turn on fog
	if (tpIntoDungeon)
		dungeons.startDungeonFog(player);

	// Fade screen
	if (tpIntoDungeon) {
		dungeons.enterCameraFade(player, dungeonId);
	} else {
		dungeons.exitCameraFade(player);
	}

	if (player.hasTag("hasFury")) {
		const circle = await utils.getEntityNearEntity("goe_bos:axe_glowing_circle", player);
		if (circle) circle.remove();
	}

	system.runTimeout(() => { teleportPlayerStep2(player, portal, tpIntoDungeon); }, 25);
}

export async function teleportPlayerStep2(player, portal, tpIntoDungeon)
{
	let tpLocation = tpIntoDungeon ? portal.getDynamicProperty("goe_bos_tp_location") : player.getDynamicProperty("goe_bos_tp_back");
	let tpRotation = tpIntoDungeon ? portal.getDynamicProperty("goe_bos_tp_rotation") : player.getDynamicProperty("goe_bos_rotation_back");
	let dim = tpIntoDungeon ? "the_end" :"overworld";
	let messageComplete = tpIntoDungeon ? "Teleport to dungeon complete" : "Teleport out of dungeon complete";
	
	// Check if dungeon is finished
	let dungeonEntity = !tpIntoDungeon ? await utils.getEntityNearEntity("goe_bos:dungeon", player) : undefined;
	const dungeonFinished = dungeonEntity ? dungeonEntity.hasTag("goe_bos_dungeon_finished") : false;
	const playerCount = dungeonEntity ? (await utils.getEntitiesInRange("minecraft:player", utils.the_end, dungeonEntity.location, 0, 100)).length : 0;

	if (tpIntoDungeon) {
		player.addTag("goe_bos_in_dungeon");
		player.addTag("spider_nest");
	} else {
		player.removeTag("goe_bos_in_dungeon");
		player.removeTag("spider_nest");

		if (dungeonFinished && playerCount === 1)
			dungeons.deleteDungeon(dungeonEntity);
	}

	// let rotY = 180;
	// let command = `execute in ${dim} run tp @s ${tpLocation.x} ${tpLocation.y} ${tpLocation.z} 180 0`
	const targetDimenion = world.getDimension(dim); 
	await utils.showPlayerInfoMessage(player, messageComplete);
	// await utils.runPlayerCommand(player, command);
	player.teleport(tpLocation, { dimension: targetDimenion });
	player.teleport(tpLocation, { rotation: {x: 0, y: tpRotation } });

	// utils.debug("TP Location: " + tpLocation.x.toFixed(2) + " " + tpLocation.y.toFixed(2) + " " + tpLocation.z.toFixed(2));

	await utils.runPlayerCommand(player, "inputpermission set @s movement enabled");
	await utils.runPlayerCommand(player, "inputpermission set @s camera enabled");
	await utils.runPlayerCommand(player, "particle goe_bos:teleport ~ ~ ~");
	await utils.playSound(player, "@a","goe_bos:portal_teleport");
	//await player.removeTag("goe_bos_teleport_active");

	// if (player.hasTag("hasFury")) {
	// 	system.runTimeout(() => {
	// 		player.dimension.spawnEntity("goe_bos:axe_glowing_circle", player.location);
	// 		utils.runPlayerCommand(player, "ride @e[type=goe_bos:axe_glowing_circle,r=1,c=1] start_riding @s");
	// 		utils.runPlayerCommand(player, "tag @e[type=goe_bos:axe_glowing_circle,c=1] add invunerable");
	// 	}, 20);
	// }

	if (tpIntoDungeon) {
		// system.runTimeout(() => { teleportPlayerStep3(player); }, 100);
	} else {
		await dungeons.clearPlayerDungeonEffects(player);
		if (dungeonFinished && playerCount === 1) {
			const runId = system.runInterval(async () => {
				const overworldPortal = await utils.getEntityNearEntity("goe_bos:portal", player);
				if (overworldPortal && overworldPortal.isValid()) {
					overworldPortal.remove();
					system.clearRun(runId);
				}
			}, 1);
		}
	}
}

export async function teleportPlayerStep3(player) {
	await dungeons.displayDungeonTitle(player);
}

export async function teleportPlayerStep4(player) {
	
}

export async function onPlayerSpawn(event) {
	const player = event.player;
	dungeons.clearPlayerDungeonEffects(player);
}

export async function onScriptEventReceive(event) {
	const entity = event.sourceEntity;

    if (event.message === "DROP") {
		if (!entity.hasTag("goe_bos_portal_is_active")) {
			entity.setProperty("goe_bos:hit_counter", 0)
			entity.triggerEvent("goe_bos:reset");
			return;
		}

		const index = entity.getProperty("goe_bos:type") - 1;
		const dungeonId = dungeonIds[index];
		const newPortal = new ItemStack(dungeonId);
		newPortal.setDynamicProperty("goe_bos_tp_location", entity.getDynamicProperty("goe_bos_tp_location"));
		newPortal.setDynamicProperty("goe_bos_tp_rotation", entity.getDynamicProperty("goe_bos_tp_rotation"));
		entity.dimension.spawnItem(newPortal, entity.location);
		entity.remove();
	}
}