import { world, system, ItemStack, EntityDamageCause, PlayerInteractWithBlockAfterEvent } from "@minecraft/server";
import { itemMap } from "./item";
import { modifyStamina, playerStamina } from "../stamina";

let weaponTickList = new Map();
weaponTickList.set("medium", { tickTotal: (2.2 * 20), tick1: 8, tick2: 18, tick3: 31 });
weaponTickList.set("long", { tickTotal: (2.75 * 20), tick1: 4, tick2: 16, tick3: 45 });
weaponTickList.set("heavy", { tickTotal: (5.75 * 20), tick1: 20, tick2: 70, tick3: 100 });
weaponTickList.set("short", { tickTotal: (1.5 * 20), tick1: 3, tick2: 15, tick3: 28 });

let throwableMap = new Map();
let cooldownMap = new Map();

world.beforeEvents.itemUse.subscribe((data) => {
    const player = data.source;
    const item = data.itemStack;
    let tagName = false;

    if (!item.hasTag("msft:combo_item")) return;
    for (const tag of player.getTags()) {
        if (tag.startsWith("unlocked:")) {
            let tagNameTemp = tag.replace("unlocked:", "");
            const itemName = item.typeId.split("_")[1];
            if (itemName === tagNameTemp) {
                tagName = tagNameTemp;
                break;
            } else {
                tagName = true;
            }
        }
    }

    if (tagName == true) {
        player.sendMessage("§cYou don't have unlocked this weapon!");
        data.cancel = true;
    } else if (tagName == false || tagName == null || tagName == undefined) return;
    
    else {
        if (playerStamina.get(player) <= 10) return;
        system.run(() => {

            if (item.getComponent('cooldown') !== null && item.getComponent('cooldown').getCooldownTicksRemaining(player) <= 0) {
                cooldownMap.delete(player.id);
                return;
            }

            if (cooldownMap.has(player.id) && cooldownMap.get(player.id) == "InCooldown") {
                return;
            }

            player.addTag(`usingItem.${item.typeId}`);
            playerUseItem(player, item);
        });
    }
});

world.afterEvents.itemCompleteUse.subscribe((data) => {
    const player = data.source;
    const item = data.itemStack;

    if (player.hasTag(`usingItem.${item.typeId}`)) {
        player.removeTag(`usingItem.${item.typeId}`);
        let hasTag = player.getTags().find(tag => tag.startsWith("damage_"));
        if (hasTag) setItemDamage(player, item, hasTag);
        let hasCooldown = item.getComponent('cooldown');
        if (hasCooldown) hasCooldown.startCooldown(player);
        cooldownMap.set(player.id, "inCooldown");
        system.runTimeout(() => {
            cooldownMap.delete(player.id);
        }, hasCooldown.getCooldownTicksRemaining(player) || 40);
    }
});

world.afterEvents.itemReleaseUse.subscribe((data) => {
    const player = data.source;
    const item = data.itemStack;

    if (!item) return;
    if (player.hasTag(`usingItem.${item.typeId}`)) {
        player.removeTag(`usingItem.${item.typeId}`);
        let hasTag = player.getTags().find(tag => tag.startsWith("damage_"));
        if (hasTag) setItemDamage(player, item, hasTag);
        let hasCooldown = item.getComponent('cooldown');
        if (hasCooldown) hasCooldown.startCooldown(player);
        cooldownMap.set(player.id, "inCooldown");
        system.runTimeout(() => {
            cooldownMap.delete(player.id);
        }, hasCooldown.getCooldownTicksRemaining(player) || 40);
    }
});

world.afterEvents.itemStopUse.subscribe((data) => {
    const player = data.source;
    const item = data.itemStack;

    if (!item) return;
    if (player.hasTag(`usingItem.${item.typeId}`)) {
        player.removeTag(`usingItem.${item.typeId}`);
        let hasTag = player.getTags().find(tag => tag.startsWith("damage_"));
        if (hasTag) setItemDamage(player, item, hasTag);
        let hasCooldown = item.getComponent('cooldown');
        if (hasCooldown) hasCooldown.startCooldown(player);
        cooldownMap.set(player.id, "inCooldown");
        system.runTimeout(() => {
            cooldownMap.delete(player.id);
        }, hasCooldown.getCooldownTicksRemaining(player) || 40);
    }
});

function setItemDamage(player, item, damage) {
    const itemEnchantmentComp = item.getComponent("minecraft:enchantable");
    const unbreakingLevel = itemEnchantmentComp?.getEnchantment("unbreaking")?.level ?? 0;

    const breakChance = 100 / (unbreakingLevel + 1);
    const randomizeChance = Math.random() * 100;

    if (breakChance < randomizeChance) return;

    const itemUsedDurabilityComp = item.getComponent("durability");
    if (!itemUsedDurabilityComp) return;
    itemUsedDurabilityComp.damage += parseInt(damage.split("_")[1]);
    //console.log("vediamo che esce", parseInt(damage.split("_")[1]));

    const maxDurability = itemUsedDurabilityComp.maxDurability;
    const currentDamage = itemUsedDurabilityComp.damage;

    const playerEquippableComp = player.getComponent("equippable");
    if (!playerEquippableComp) return;

    if (currentDamage >= maxDurability) {
        playerEquippableComp.setEquipment("Mainhand", new ItemStack('minecraft:air', 1));
    } else {
        playerEquippableComp.setEquipment("Mainhand", item);
    }
    player.removeTag(damage);
    //console.log(maxDurability, currentDamage);
}

function playerUseItem(player, item) {
    let weaponType;
    const actItemId = item.typeId;

    switch (true) {
        case actItemId.includes("battleaxe"):
        case actItemId.includes("claymore"):
        case actItemId.includes("greathammer"):
        case actItemId.includes("hammer"):
        case actItemId.includes("mace"):
            weaponType = "heavy";
            break;
        case actItemId.includes("glaives"):
        case actItemId.includes("spear"):
            weaponType = "long";
            break;
        case actItemId.includes("axe"):
        case actItemId.includes("cutlass"):
        case actItemId.includes("katana"):
        case actItemId.includes("machete"):
        case actItemId.includes("scythe"):
        case actItemId.includes("sword"):
            weaponType = "medium";
            break;
        case actItemId.includes("dagger"):
        case actItemId.includes("knife"):
            weaponType = "short";
            break;
        default:
            return;
    }
    itemAttack(player, item, weaponType);
}

function TickEvent(player, item, code, ticks) {
    let stop = false;
    for (let i = 0; i <= ticks; i++) {
        const runTimeout = ticks - (ticks * (i / (ticks)));
        system.runTimeout(() => {
            if (cooldownMap.has(player.id) && cooldownMap.get(player.id) === "inCooldown") {
                stop = true;
                return;
            }

            if (stop == true) {
                return;
            }

            if (!(player && player.isValid() && item && player.hasTag(`usingItem.${item.typeId}`))) {
                stop = true;
                return;
            }
            code(player, item, runTimeout);
        }, runTimeout);
    }
}

function itemAttack(player, item, weaponType) {
    let { tickTotal, tick1, tick2, tick3 } = weaponTickList.get(weaponType);
    TickEvent(player, item, (player, item, currentTick) => {
        if (currentTick != tick1 && currentTick != tick2 && currentTick != tick3) return;
        const viewDirection = player.getViewDirection();
        const center = { x: player.location.x, y: player.location.y + 1, z: player.location.z };
        const hitLocation = { x: center.x + (viewDirection.x * 2), y: center.y + (viewDirection.y * 2), z: center.z + (viewDirection.z * 2) };

        const { damage, range } = itemMap.get(item.typeId);
        let entities = player.dimension.getEntities({ location: hitLocation, maxDistance: range, excludeTypes: ["item", "xp_orb", "arrow", "falling_block", "thrown_trident"] });

        let damageAct = damage;

        // switch (currentTick) {
        //     case tick1:
        //         console.log("damage", tick1, damageAct);
        //         break;
        //     case tick2:
        //         console.log("damage", tick2, damageAct);
        //         break;
        //     case tick3:
        //         //damageAct = damage * (1 + (comboMolt / 100));
        //         console.log("cccc", tick3, damageAct);
        //         break;

        // }

        const bonusAttacker = player.getDynamicProperty("damage");
        let damageExtra = damageAct * (bonusAttacker / 100);

        for (let i = 0; i < entities.length; i++) {
            if (entities[i].id == player.id) continue;
            if (bonusAttacker >= 10) {
                entities[i].applyDamage(damageExtra, { cause: "entityAttack", damagingEntity: player });
            } else {
                entities[i].applyDamage(damageAct, { cause: "entityAttack", damagingEntity: player });
            }

            switch (currentTick) {
                case tick1:
                    modifyStamina(player, 5, "remove");
                    player.playSound(`msft:swing:${weaponType}.1`, { x: player.location.x, y: player.location.y, z: player.location.z });
                    player.addTag("damage_1");
                    player.removeTag("damage_2");
                    player.removeTag("damage_3");
                    break;
                case tick2:
                    modifyStamina(player, 10, "remove");
                    player.playSound(`msft:swing:${weaponType}.2`, { x: player.location.x, y: player.location.y, z: player.location.z });
                    player.removeTag("damage_1");
                    player.addTag("damage_2");
                    player.removeTag("damage_3");
                    break;
                case tick3:
                    modifyStamina(player, 15, "remove");
                    player.playSound(`msft:swing:${weaponType}.3`, { x: player.location.x, y: player.location.y, z: player.location.z });
                    player.removeTag("damage_1");
                    player.removeTag("damage_2");
                    player.addTag("damage_3");
                    const comboAttacker = player.getDynamicProperty("combo");
                    let damageExtra3 = damageAct * (comboAttacker / 100);

                    entities[i].applyDamage(damageExtra3, { cause: "entityAttack", damagingEntity: player });
                    break;
            }
        }
    }, tickTotal);
}

world.afterEvents.itemUse.subscribe((data) => {
    const { itemStack, source: player } = data;
    const dimension = player.dimension;

    const echoForge = player.getEntitiesFromViewDirection({
        type: "msft:echo_forge",
        maxDistance: 5,
    });

    if (echoForge.length > 0) return;

    if (itemStack.typeId.includes("boomerang") || itemStack.typeId.includes("shuriken") || itemStack.typeId.includes("sai")) {
        if (itemStack.getComponent('cooldown').getCooldownTicksRemaining(player) > 0) return;
        const viewDirection = player.getViewDirection();
        const spawnLocation = {
            x: player.location.x + viewDirection.x,
            y: player.location.y + viewDirection.y + 1.8,
            z: player.location.z + viewDirection.z,
        };
        player.playAnimation('animation.throwable_use');
        system.runTimeout(() => {
            if (itemStack.typeId.includes("boomerang")) player.runCommandAsync(`tag @s add "${player.id}"`);
            throwableMap.set(player.id, itemStack);
            // const playerEquippableComp = player.getComponent("equippable");
            // if (!playerEquippableComp) return;
            // playerEquippableComp.setEquipment("Mainhand", new ItemStack('minecraft:air', 1));
            let playerInventory = player.getComponent("inventory");
            if (playerInventory) {
                let container = playerInventory.container;
                let slot = container.getSlot(player.selectedSlotIndex);

                if (slot && slot.amount > 1) slot.amount -= 1;
                else container.setItem(player.selectedSlotIndex, new ItemStack('minecraft:air', 1));
            }

            let shootEntity = dimension.spawnEntity(itemStack.typeId.replace("_item", ""), spawnLocation);
            let shootProjectileComp = shootEntity.getComponent('minecraft:projectile');
            shootProjectileComp.owner = player;
            let shootingSpeed = 1;
            if (!itemStack.typeId.includes("boomerang")) {
                shootingSpeed = 2;
            }
            let velocity = {
                x: viewDirection.x * shootingSpeed,
                y: viewDirection.y * shootingSpeed,
                z: viewDirection.z * shootingSpeed
            };
            shootProjectileComp.shoot(velocity);
            shootEntity.addTag(player.id);

            if (itemStack.typeId.includes("boomerang")) itemStack.getComponent('cooldown').startCooldown(player);
        }, 3);
    }
});

system.afterEvents.scriptEventReceive.subscribe(async (event) => {
    const { id, sourceEntity, message } = event;

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

    const players = sourceEntity.dimension.getEntities({
        type: "minecraft:player",
    });
    if (id === "msft:kill_throwable") {
        sourceEntity.kill();
    }

    if (id === "msft:kill_boomerang") {
        for (const player of players) {

            const echoForge = player.dimension.getEntities({
                type: "msft:echo_forge",
                location: player.location,
                maxDistance: 5,
                closest: 1,
            })[0];

            if (echoForge) {
                event.cancel = true;
                continue;
            }

            if (!sourceEntity.hasTag(player.id)) {
                continue;
            }

            const itemStack = throwableMap.get(player.id);

            const throwable = player.dimension.getEntities({
                type: itemStack.typeId.replace("_item", ""),
                location: player.location,
                maxDistance: 5
            })[0];

            if (throwable) {
                const itemEnchantmentComp = itemStack.getComponent("minecraft:enchantable");
                const unbreakingLevel = itemEnchantmentComp?.getEnchantment("unbreaking")?.level ?? 0;

                const breakChance = 100 / (unbreakingLevel + 1);
                const randomizeChance = Math.random() * 100;

                if (breakChance < randomizeChance) return;

                const itemUsedDurabilityComp = itemStack.getComponent("durability");
                if (!itemUsedDurabilityComp) return;
                itemUsedDurabilityComp.damage += 1;

                const maxDurability = itemUsedDurabilityComp.maxDurability;
                const currentDamage = itemUsedDurabilityComp.damage;

                const playerEquippableComp = player.getComponent("equippable");
                if (!playerEquippableComp) return;

                if (currentDamage >= maxDurability) {
                    playerEquippableComp.setEquipment("Mainhand", new ItemStack('minecraft:air', 1));
                } else {
                    playerEquippableComp.setEquipment("Mainhand", itemStack);
                }
            }
        }
    }
});

system.runInterval(() => {
    for (const player of world.getPlayers()) {
        player.runCommandAsync(`execute at @e[tag=msft:boomerang_return] run tp @e[tag=msft:boomerang_return] ^ ^ ^0.2 facing @p[tag="${player.id}"]`);
        player.runCommandAsync(`execute at @e[tag=msft:boomerang_return] run tp @e[tag=msft:boomerang_return] ^ ^ ^0.2 facing @p[tag="${player.id}"]`);
        player.runCommandAsync(`execute at @e[tag=msft:boomerang_return] run tp @e[tag=msft:boomerang_return] ^ ^ ^0.2 facing @p[tag="${player.id}"]`);
    }
});