import {world,system} from '@minecraft/server';
import {skillRequirements,invItemEffects,armourEffects,mainHandItemEffects,offHandItemEffects} from '../config.js';
import {addReqLore,cancelEvent,cancelArmorEquipment,classSelection,Utils,applyEffect,giveItemEffects,removeItemEffects,getAttributes,setAttributes} from '../utils.js';
import { debug, Check, getCooldownTime, clamp, healthParticle, toColor } from "../mathAndCalculations.js";

system.runInterval(() => {
	for (const player of world.getAllPlayers()) {
		player.dimension.runCommandAsync('difficulty h');
		const mainHand = player.getComponent('equippable')?.getEquipment('Mainhand');
		if (mainHand && mainHand.typeId === 'minecraft:compass') {
			player.onScreenDisplay.setActionBar(`Position: ${Math.trunc(player.location.x)}, ${Math.trunc(player.location.y)}, ${Math.trunc(player.location.z)}`);
		}
		// Constantly checks status, also sends data to UI
		const status = player.getStatus();
		const { equippableComp, item, stats } = player.getItemStats();
		const currentTime = Date.now();
		
		// If the player has shield up, run delay check
		// Crucial for making sure the attacker does not get knocked back
		if (Check.shield(player)) {
			status.shieldDelay += 1;
		} else {
			status.shieldDelay = 0;
		}
		status.shieldValid = status.shieldDelay > 5;
		
		// If the player changes the slot, run cooldown
		const slotChangeCheck = (player.selectedSlotIndex === status.lastSelectedSlot) && (status.lastSelectedItem === item?.typeId);
		if (!slotChangeCheck) {
			status.lastAttackTime = currentTime;
		}
		status.lastSelectedSlot = player.selectedSlotIndex;
		status.lastSelectedItem = item?.typeId;
		
		// For UI
		status.cooldown = Math.max(0,(status.lastAttackTime + getCooldownTime(player, stats?.attackSpeed).ms - currentTime)/50);
		
		// Sprint check
		if (!player.isSprinting) {
            status.sprintKnockbackHitUsed = false;
            status.sprintKnockbackValid = false;
            status.critSweepValid = true;
        } else if (player.isSprinting && !status.sprintKnockbackHitUsed) {
            status.sprintKnockbackValid = true;
        } else if (player.isSprinting && status.sprintKnockbackHitUsed) {
            status.sprintKnockbackValid = false;
        }
        status.critSweepValid = !player.isSprinting || status.sprintKnockbackHitUsed;
        
        // Fall distance code by Jayly
        // For mace smash attack
        const fallDist = status.fallDistance;
		if (player.isFalling) {
			status.fallDistance = fallDist + player.getVelocity().y;
		} else {
			system.run(() => status.fallDistance = 0);
		}
		
		// If the player falls more than 1.5 blocks, trigger damage event so that mace smash can work properly
        if (world.getDynamicProperty("addon_toggle") == true) {
	        if (Math.abs(fallDist) >= 1.5 && item?.typeId === "minecraft:mace") {
				player.triggerEvent("sweepnslash:mace");
				status.mace = true;
			} else {
				player.triggerEvent("sweepnslash:not_mace");
				status.mace = false;
			}
		} else player.triggerEvent("sweepnslash:mace");
		
		// For UI
		const maxCD = getCooldownTime(player, stats?.attackSpeed).ticks;
		const curCD = status.cooldown;
		const cooldownPercentage = ((maxCD - curCD) / maxCD) * 100;
		
		let subGrey = cooldownPercentage / 10;
		let subDarkGrey = Math.floor(10 - subGrey)
		let cooldownSubtitle = '§7˙'.repeat(subGrey)
		cooldownSubtitle += '§8˙'.repeat(subDarkGrey)
		
		const inRange = Check.view(player);
		const targetValid = !(inRange?.getComponent("health")?.currentValue <= 0);
		const specialCheck = Check.specialValid(currentTime, player, stats);
		
		const riders = player.getRiders() || [];
		const riderCheck = riders.some(rider => rider === inRange);
		const ridingCheck = player.getRidingOn() !== inRange
		
		const viewCheck = (inRange && targetValid && !riderCheck && ridingCheck);
		
		// Handles indicators
		// If the player has indicator disabled, the title will show up once to clean up the UI and never appear
		const barStyle = player.getDynamicProperty("cooldownStyle") ?? 0;
		const barArray = ["crs", "htb", "sub", "non"][barStyle];
		
		const bonkReady = specialCheck && viewCheck;
		
		if (barStyle === 3) {
		    if (status.showBar) {
		        player.onScreenDisplay.setTitle('_sweepnslash:non', {
					fadeInDuration: 0,
					fadeOutDuration: 0,
					stayDuration: 0
				});
		        status.showBar = false;
		    }
		} else { 
		    status.showBar = true;
		    if (curCD > 0 || viewCheck) {
		        barStyle !== 2 ? 
					player.onScreenDisplay.setTitle(`_sweepnslash:${barArray}:${bonkReady ? 't' : 'f'}:${cooldownPercentage.toFixed(0)}`, {
						fadeInDuration: 0,
						fadeOutDuration: 0,
						stayDuration: 0
					})
				: player.onScreenDisplay.setTitle(' ', {
					fadeInDuration: 0,
					fadeOutDuration: 0,
					stayDuration: 10,
					subtitle: `${cooldownSubtitle}`
				});
		        status.attackReady = false;
		    } else if (curCD <= 0 && status.attackReady == false) {
		        player.onScreenDisplay.setTitle('_sweepnslash:non', {
					fadeInDuration: 0,
					fadeOutDuration: 0,
					stayDuration: 0
				});
		        status.attackReady = true;
		    }
		}
		
		// Debug function for developing
		if (debug()) {
			const velocity = (Math.hypot(player.getVelocity().x, player.getVelocity().y, player.getVelocity().z)*20).toFixed(2)
			const actionBarDisplay = `${status.cooldown.toFixed(0)}\n§f${velocity}`
			player.onScreenDisplay.setActionBar(actionBarDisplay);
		}
		// Reforge speed attribute
		const movement = player.getComponent('movement');
		let playerAttributes = {
			attackDamage: 1,
			projectileDamage: 1,
			criticalChance: 0,
			criticalDamage: 1.5,
			blockChance: 0,
			parryChance: 0,
			damageReduction: 1,
			evasionChance: 0,
			movementSpeed: 1
		};
		getAttributes(player, playerAttributes);
		movement.setCurrentValue(player.isSprinting ? 0.12999999523162842 * playerAttributes.movementSpeed : 0.10000000149011612 * playerAttributes.movementSpeed);
	};
	// Flying mechanics
    const dim = world.getDimension('overworld');
    for (const entity of dim.getEntities({ families: [ 'sc:can_fly' ] })) {
        const utils = new Utils(entity);
        // Recommended values
        if (entity.typeId == 'sc:pyre_drake' || entity.typeId == 'sc:glacis_drake') utils.flySystem(0.09, 0.004, 2);
        else if (entity.typeId == 'sc:wretch') utils.flySystem(0.09, 0.004, 4);
    };
});

system.runInterval(() => {
	for (const player of world.getAllPlayers()) {
		// Reset dynamic properties
		const dynamicProperties = [
			'clSpeed', 'clStrength', 'clResistance', 'clJumpBoost', 
			'clRegeneration', 'clHaste', 'clAbsorption', 
			'clWaterBreathing', 'clFireResistance', 'clNightVision', 
			'clSlowFalling', 'clVillageHero'
		];
		for (let i = 0; i < dynamicProperties.length; i++) player.setDynamicProperty(dynamicProperties[i], 0);

		const inventory = player.getComponent('inventory').container;
		const equipment = player.getComponent('equippable');
		const slots = ['Head', 'Chest', 'Legs', 'Feet', 'Mainhand', 'Offhand'];

		for (let i = 0; i < slots.length; i++) {
			const slot = slots[i];
			const item = equipment.getEquipment(slot);
			if (!item) continue;

			const typeId = item.typeId;
			const armorItem = skillRequirements['armors'][typeId];

			// Cancel armor equipment
			if (slot !== 'Mainhand' && slot !== 'Offhand') {
				cancelArmorEquipment(armorItem, player, item, slots[i]);
			}

			// Apply effects from armor and weapons
			if (slot === 'Mainhand' && mainHandItemEffects[typeId]) {
				giveItemEffects(mainHandItemEffects[typeId], player);
			} else if (slot === 'Offhand' && offHandItemEffects[typeId]) {
				giveItemEffects(offHandItemEffects[typeId], player);
			} else if (armourEffects[typeId]) {
				giveItemEffects(armourEffects[typeId], player);
			}

			// Apply lore-based effects
			const lore = item.getLore();
			if (slot === 'Chest' && lore.includes('§r§7Vitality I')) {
				player.setDynamicProperty('clRegeneration', player.getDynamicProperty('clRegeneration') + 1);
			}
			if (slot === 'Legs') {
				if (lore.includes('§r§7Agility II')) {
					player.setDynamicProperty('clSpeed', player.getDynamicProperty('clSpeed') + 2);
				} else if (lore.includes('§r§7Agility I')) {
					player.setDynamicProperty('clSpeed', player.getDynamicProperty('clSpeed') + 1);
				}
			}
			if (slot === 'Feet') {
				if (lore.includes('§r§7Light Weight I')) {
					player.setDynamicProperty('clSlowFalling', player.getDynamicProperty('clSlowFalling') + 1);
				}
				if (lore.includes('§r§7Aerial Affinity I')) {
					player.setDynamicProperty('clHaste', player.getDynamicProperty('clHaste') + 1);
				}
				if (lore.includes('§r§7High Jump I')) {
					player.setDynamicProperty('clJumpBoost', player.getDynamicProperty('clJumpBoost') + 3);
				}
			}
		}

		// Process inventory items
		const processedItems = new Set();
		for (let i = 0; i < inventory.size; i++) {
			const item = inventory.getItem(i);
			if (!item) continue;

			const typeId = item.typeId;
			const itemTags = [
				"minecraft:is_sword", "minecraft:is_axe", "minecraft:is_pickaxe", 
				"minecraft:is_shovel", "minecraft:is_hoe", "minecraft:is_armor", 
				"minecraft:is_trident", "minecraft:bow", "minecraft:crossbow", 
				"minecraft:mace", "minecraft:shield"
			];

			// Set attributes if doesn't have
			if (itemTags.some(tag => item.hasTag(tag))) {
				const lore = item.getLore();
				if (!lore.some(line => line.includes('\xA7r\xA7a\xA7t\xA7t\xA7r\xA7i\xA7b\xA7u\xA7t\xA7e\xA7s\xA7r'))) {
					setAttributes(item);
					inventory.setItem(i, item);
				}
			}

			// Handle item requirements lore
			const itemReqs = [
				skillRequirements['items'][typeId],
				skillRequirements['armors'][typeId],
				skillRequirements['blocks'][typeId],
				skillRequirements['shields'][typeId]
			];
			for (const req of itemReqs) {
				if (!req) continue;
				const requirements = Array.isArray(req.requirements) ? req.requirements : [req.requirements];
				requirements.forEach(req => addReqLore(req, player, inventory, item, i));
			}

			// Apply item effects
			const effects = invItemEffects[typeId];
			if (effects && !processedItems.has(typeId) && i > 8) {
				giveItemEffects(effects, player);
				processedItems.add(typeId);
			}
		}

		// Apply effects
		const effectsToApply = ['clSpeed','clStrength','clResistance','clJumpBoost','clHaste','clAbsorption'];
		for (let i = 0; i < effectsToApply.length; i++) applyEffect(player, effectsToApply[i]);

		if (player.getDynamicProperty('clRegeneration') && (player['regCooldown'] ?? 0) < Date.now()) {
			applyEffect(player, 'clRegeneration');
			player['regCooldown'] = Date.now() + 2500;
		}
		if (player.getDynamicProperty('clWaterBreathing') >= 1) {
			player.addEffect('water_breathing', 20, { showParticles: false });
		}
		if (player.getDynamicProperty('clFireResistance') >= 1) {
			player.addEffect('fire_resistance', 20, { showParticles: false });
		}
		const block = player.dimension.getBlock(player.location);
		if (player.getDynamicProperty('clSlowFalling') >= 1 && !player.isFlying && !player.isGliding && !player.isJumping && block.isAir && block.below().isAir && block.below(2).isAir && block.below(3).isAir && block.below(4).isAir) {
			player.addEffect('slow_falling', 20, { showParticles: false });
		}
		if (player.getDynamicProperty('clNightVision') >= 1) {
			player.addEffect('night_vision', 11 * 20, { showParticles: false });
		}
		if (player.getDynamicProperty('clVillageHero') >= 1) {
			player.addEffect('village_hero', 20, { showParticles: false });
		}
	}
}, 10);

system.runInterval(() => {
	for (const player of world.getAllPlayers()) {
		const mainHand = player.getComponent('equippable').getEquipment('Mainhand');
		const offHand = player.getComponent('equippable').getEquipment('Offhand');
		let mainHandItem;
		let offHandItem;
		if (mainHand) mainHandItem = skillRequirements['shields'][mainHand.typeId];
		if (offHand) offHandItem = skillRequirements['shields'][offHand.typeId];
		const item = mainHandItem ?? offHandItem;
		if (item) {
			let inBetweenText = '';
			if (Array.isArray(item.requirements)) {
				item.requirements.forEach(req => {
					const skill = player.getDynamicProperty(req.skill) ?? 0;
					inBetweenText += `${req.skill} ${req.level}`;
					if (index < source.requirements.length - 1) inBetweenText += ' & ';
					if (item && skill < req.level && player.isSneaking) {
						mainHandItem ? mainHand.getComponent('cooldown').startCooldown(player) : offHand.getComponent('cooldown').startCooldown(player);
						player.onScreenDisplay.setActionBar(`§4Required §9Knowledge of ${inBetweenText} §fto use the item`);
					}
				});
			} else {
				const skill = player.getDynamicProperty(item.requirements.skill) ?? 0;
				if (item && skill < item.requirements.level && player.isSneaking) {
						mainHandItem ? mainHand.getComponent('cooldown').startCooldown(player) : offHand.getComponent('cooldown').startCooldown(player);
						player.onScreenDisplay.setActionBar(`§4Required §9Knowledge of ${item.requirements.skill} ${item.requirements.level} §fto use the item`);
				}
			}
		}
	}
}, 5);
