import { world, system, Player } from "@minecraft/server";
import { ActionFormData, MessageFormData, ModalFormData } from "@minecraft/server-ui";
import { ScriptEventListener } from "../utils/scriptevent.js";
import { Debugger } from "../utils/debug.js";

const debug = new Debugger('mythicus:dev');
debug.enabled = false;
debug.priority = 1;

// global variables
const developerModeEnabled = true;
const overworld = world.getDimension('overworld');

let worldPropertiesClearTimer = 0;

const item_functions_dict = {
	'minecraft:diamond_hoe': (player) => TeleportThrough(player),
};

const devHelp_pages = {
	1: [
		"",
		"§a--- §2mythicus:dev.§a help page 1 of 2 ---§r",
		"§7Event: §ghelp <?page>",
		"§8Desc: §fList all dev events [begin with 'mythicus:dev.']. Optional argument for page.",
		"§7Event: §gplayer.properties <?player>",
		"§8Desc: §fList all properties for player. Optional argument for player name.",
		"§7Event: §gworld.properties",
		"§8Desc: §fList all properties for world.",
		"§7Event: §gworld.properties.clear",
		"§8Desc: §fClear all world properties.",
	],
	2: [
		"",
		"§a--- §2mythicus:dev.§a help page 2 of 2 ---§r",
		"§7Event: §gdebug.set <priority>",
		"§8Desc: §fSet global priority level.",
		"§7Event: §gdebug.enableAll",
		"§8Desc: §fEnable all debuggers.",
		"§7Event: §gdebug.disableAll",
		"§8Desc: §fDisable all debuggers.",
	]
}

const script_functions_dict = {
	help: {
		func: DevHelp,
		args: ['player', 'message'],
	},
	'player.properties': {
		func: DevPlayerProperties,
		args: ['player', 'message'],
	},
	'world.properties': {
		func: DevWorldProperties,
		args: ['player'],
	},
	'world.properties.clear': {
		func: DevWorldPropertiesClear,
		args: ['player'],
	},
};

// command: /scriptevent mythicus:dev.help
ScriptEventListener.subscribe('mythicus:dev.', (event) => {

	let identifier = event.id;

	// exit event if not called by player
	if (event.sourceEntity.typeId != "minecraft:player") {
		world.sendMessage("ERROR: dev (mythicus:dev) events must be initialized by a player.")
		return;
	}

	let actionIndex = identifier.indexOf('.');
	let action = actionIndex !== -1 ? identifier.substring(actionIndex + 1) : '';
	let player = event.sourceEntity;
	let message = event.message ?? -1;

	if (!(action in script_functions_dict)) {
		player.sendMessage("§7Try: §g/scriptevent mythicus:dev.help");
		return;
	};

	const funcAvailableArgs = { player: player, message: message, action: action };

	const funcEntry = script_functions_dict[action];

	let funcArgs = [];
	funcEntry.args.forEach(arg => {
		if (arg in funcAvailableArgs) {
			funcArgs.push(funcAvailableArgs[arg]);
		}
	});

	if (funcEntry && funcArgs && funcArgs.length === funcEntry.args.length) {
		funcEntry.func(...funcArgs);
	} else {
		player.sendMessage("Invalid event data/args. Try: /scriptevent mythicus:dev.help");
	}

}, { isPrefix: true });


function DevHelp(player, message) {

	let page = message == '' ? 1 : parseInt(message);
	if (isNaN(page)) {
		debug.warn(`Input page [${message}] must be a number.`);
		return;
	}

	page = Math.min(Math.max(page, 1), 2);
	let info = devHelp_pages[page] ?? undefined;
	if (!info) {
		debug.warn(`Invalid page number [${page}].`);
		return;
	}

	player.sendMessage(info.map(line => line + "\n"));

};

function DevPlayerProperties(player, message) {

	let targetPlayer = player;

	if (message !== "") {
		targetPlayer = world.getPlayers({ name: message })[0];

		if (targetPlayer == undefined) {
			player.sendMessage('\n§4ERROR: This player does not exist.');
			return;
		}
	}

	const dynamicProperties = targetPlayer.getDynamicPropertyIds().sort(DynamicPropertiesSort);

	if (dynamicProperties.length === 0) {
		player.sendMessage('\n§4This player has no dynamic properties.');
		return;
	}

	player.sendMessage('\n§aPlayer dynamic properties:');
	player.sendMessage(
		dynamicProperties.map((property) => {
			const value = targetPlayer.getDynamicProperty(property) ?? `null`;
			return `§f${property}: §7${typeof value === 'object' ? JSON.stringify(value) : value}\n`
		})
    );

};

function DevWorldProperties(player) {

	const dynamicProperties = world.getDynamicPropertyIds().sort(DynamicPropertiesSort);

	if (dynamicProperties.length === 0) {
		player.sendMessage('\n§4There are no world dynamic properties.');
		return;
	}

	player.sendMessage('\n§cWorld dynamic properties:');
	player.sendMessage(
		dynamicProperties.map((property) => {
			const value = world.getDynamicProperty(property) ?? `null`;
			return `§f${property}: §7${typeof value === 'object' ? JSON.stringify(value) : value}\n`
		})
	);

};

ScriptEventListener.subscribe('mythicus:dev.debug.enableAll', (event) => {
	world.sendMessage('All debuggers have been enabled.');
	Debugger.enableAll();
});

ScriptEventListener.subscribe('mythicus:dev.debug.disableAll', (event) => {
	world.sendMessage('All debuggers have been disabled.');
	Debugger.disableAll();
});

ScriptEventListener.subscribe('mythicus:dev.debug.set', (event) => {
	const priority = parseInt(event.message);
	if (isNaN(priority)) {
		debug.warn(`Message [${event.message}] must be a number.`);
		return;
	}

	world.sendMessage(`Global priority to set ${priority}.`);
	Debugger.setGlobalPriority(priority);
});


// Sorting function for dynamic property ids
function DynamicPropertiesSort(a, b) {

	// set delimiter
	const del = '_';

	// compare string length
	const lengthDiff = b.length - a.length;

	// compare delimited strings
	if (!a.includes(del) && !b.includes(del)) {
		return lengthDiff;
	} else if (!a.includes(del) && b.includes(del)) {
		return -1;
	} else if (a.includes(del) && !b.includes(del)) {
		return 1;
	}

	// compare substring before delimiter
	const aKey = a.split(del)[0];
	const bKey = b.split(del)[0];

	if (aKey.localeCompare(bKey) === 0) {
		const aNum = parseInt(a.split(del)[1].match(/\d+/), 10) || 0;
		const bNum = parseInt(b.split(del)[1].match(/\d+/), 10) || 0;

		return bNum - aNum;
	} else {
		return aKey.localeCompare(bKey);
	}
}

function DevWorldPropertiesClear(player) {

	if (worldPropertiesClearTimer == 0) {
		player.sendMessage('\nAre you sure you want to §4clear all world properties§r? Re-enter command to confirm.')

		const timeoutId = system.runTimeout(() => {
			worldPropertiesClearTimer = 0;
			player.sendMessage('\n§cWorld properties clear window timed out.');
		}, 100)

		worldPropertiesClearTimer = timeoutId;
	} else {
		system.clearRun(worldPropertiesClearTimer);
		worldPropertiesClearTimer = 0;

		world.clearDynamicProperties();
		player.sendMessage('\n§4All world properties have been cleared.');
	}

};



//world.afterEvents.itemUse.subscribe(event => {

//	const player = event.source;
//	const itemStack = event.itemStack;
//	const itemName = itemStack.typeId;

//	const cmdFeedback = overworld.runCommand('gamerule sendcommandfeedback');
//	if (cmdFeedback.successCount == 1) overworld.runCommand('gamerule sendcommandfeedback false');
	

//	if (!(itemName in item_functions_dict)) return;

//	let func = item_functions_dict[itemName];

//	func(player);

//	if (cmdFeedback.successCount == 1) overworld.runCommand('gamerule sendcommandfeedback true');

//});

let teleportThroughSelection = 0;
const teleportThrough_Values = [100, 50, 25, 10, 200];

function TeleportThrough(player) {
	if (!(player instanceof Player)) return;

	const velocity = player.getVelocity();

	if (Math.abs(velocity.x) <= 0.1 && Math.abs(velocity.y) <= 0.1 && Math.abs(velocity.z) <= 0.1 && player.getRotation().x < -89) {
		teleportThroughSelection = (teleportThroughSelection + 1) % teleportThrough_Values.length;
		player.sendMessage(`Teleport distance updated to ${teleportThrough_Values[teleportThroughSelection]}.`);
		return;
	}

	player.runCommand(`tp @s ^ ^ ^${teleportThrough_Values[teleportThroughSelection]}`);

}
