import { system, world, EquipmentSlot, EntityEquippableComponent, Effect, EffectType } from "@minecraft/server";

// #region Utils
import { getObjective, getEquipment, getPlayers, classTests, cooldownMap, commandMap, standardMap, displayMap } from "./combat_utils.js";
// #endregion
const scoreboardWeaponClass = getObjective("htc_weapon_class");
const scoreboardCooldown = getObjective("htc_attack_cooldown");
const scoreboardParryCooldown = getObjective("htc_parry_cooldown");

system.runInterval(() => {
    for (const player of world.getPlayers()) {
        const playerEquipment = getEquipment(player);
        const mainHandItem = playerEquipment?.getEquipment(EquipmentSlot.Mainhand);
        let weaponClassValue;
        try { weaponClassValue = scoreboardWeaponClass.getScore(player); } catch { weaponClassValue = 0; scoreboardWeaponClass.setScore(player, 0); }
        if (mainHandItem) {
            if (!classTests.some(classTest => classTest.some(typeId => mainHandItem.typeId.includes(typeId)))) {
                scoreboardWeaponClass.setScore(player, 0);
                player.runCommand("title @a times 0 3 3")
            }
            classTests.forEach((classTest, index) => {
                if (classTest.find(typeId => mainHandItem.typeId.includes(typeId)) && (weaponClassValue !== index + 1 || weaponClassValue === undefined)) {
                    scoreboardWeaponClass.setScore(player, index + 1);
                }
            });
        } else { scoreboardWeaponClass.setScore(player, 0); }
    }
}, 1);

world.afterEvents.entityHitEntity.subscribe((evd) => {
    const damagingEntity = evd.damagingEntity;
    const hitEntity = evd.hitEntity;
    if (damagingEntity.typeId !== "minecraft:player") return;
    if (damagingEntity && hitEntity) {
        const cooldownValue = scoreboardCooldown.getScore(damagingEntity);
        const weaponClassValue = scoreboardWeaponClass.getScore(damagingEntity);
        const isOnGroundAndNotFalling = damagingEntity.isOnGround && !damagingEntity.isFalling;
        const command = commandMap[weaponClassValue];

        const playerViewDirection = damagingEntity.getViewDirection();
        const playerDirectionX = playerViewDirection.x;
        const playerDirectionY = playerViewDirection.y;
        const playerDirectionZ = playerViewDirection.z;

        const playerEquipment = damagingEntity.getComponent(EntityEquippableComponent.componentId);
        const mainHandItem = playerEquipment?.getEquipment(EquipmentSlot.Mainhand);
        if (weaponClassValue in cooldownMap) {
            const newCooldownValue = cooldownMap[weaponClassValue];
            if ((cooldownValue <= newCooldownValue) || (cooldownValue === undefined)) { scoreboardCooldown.setScore(damagingEntity, newCooldownValue); }
            if ((cooldownValue === undefined || cooldownValue <= 0) && isOnGroundAndNotFalling && standardMap.includes(weaponClassValue)) {
                damagingEntity.runCommand(`function ${command} `);
            }
            if (mainHandItem) {
                if ((mainHandItem.typeId.includes("shield") || mainHandItem.typeId.includes("slime_") || mainHandItem.typeId.includes("_nunchaku"))) {
                    if (mainHandItem.typeId.includes("shield") && (cooldownValue === undefined || cooldownValue <= 0)) {
                        damagingEntity.dimension.getEntities({
                            excludeTypes: ["item", "painting", "ender_dragon", "npc", "arrow", "thrown_trident", "xp_orb", "snowball", "egg", "ender_pearl", "eye_of_ender_signal", "fishing_hook", "chest_boat"],
                            minDistance: 0, maxDistance: 1.4, location: hitEntity.location
                        }).forEach(e => {
                            e.applyKnockback(playerDirectionX, playerDirectionZ, 1.6, 0.35);
                            e.applyDamage(4, { cause: "entityAttack", damagingEntity: damagingEntity });
                        });
                    }
                    if (mainHandItem.typeId.includes("slime_") && !mainHandItem.typeId.includes("_nunchaku")) { hitEntity.applyKnockback(playerDirectionX, playerDirectionZ, 1.4, 0.25); }
                    if (mainHandItem.typeId.includes("_nunchaku") && !mainHandItem.typeId.includes("slime_")) { hitEntity.applyKnockback(playerDirectionX, playerDirectionZ, 0.25, 0.25); }
                    if (mainHandItem.typeId.includes("slime_") && mainHandItem.typeId.includes("_nunchaku")) { hitEntity.applyKnockback(playerDirectionX, playerDirectionZ, 0.4, 0.4); }
                }
            }
            if (weaponClassValue === 11) {
                if (!isOnGroundAndNotFalling && playerDirectionY < 0) {
                    damagingEntity.applyKnockback(playerDirectionX, playerDirectionZ, 0, 0.8);
                } else { damagingEntity.applyKnockback(playerDirectionX, playerDirectionZ, -1.3, 0) }
            }
            if (weaponClassValue === 12) {
                if (!isOnGroundAndNotFalling) {
                    damagingEntity.applyKnockback(playerDirectionX, playerDirectionZ, -1.3, -0.6);
                } else { damagingEntity.applyKnockback(playerDirectionX, playerDirectionZ, 0.8, 0.0) }
            }
            if ((cooldownValue === undefined || cooldownValue <= 0) && !isOnGroundAndNotFalling && weaponClassValue === 13) {
                damagingEntity.runCommand(`function ${command} `);
                damagingEntity.playSound("mob.wither.break_block", { location: hitEntity.location, pitch: 0.8, volume: 0.1 })
                damagingEntity.dimension.getEntities({
                    excludeTypes: ["item", "painting", "ender_dragon", "npc", "arrow", "thrown_trident", "xp_orb", "snowball", "egg", "ender_pearl", "eye_of_ender_signal", "fishing_hook", "chest_boat"],
                    minDistance: 0.5, maxDistance: 3, location: damagingEntity.location
                }).forEach(e => { e.applyKnockback(playerDirectionX, playerDirectionZ, 0, 0.8) });
            }
        }
    }
});

world.afterEvents.playerSpawn.subscribe((event) => {
    event.player.onScreenDisplay.setTitle('', {
        stayDuration: 36,
        fadeInDuration: 8,
        fadeOutDuration: 20,
        subtitle: '',
    });
});

system.runInterval(() => {
    for (const player of world.getPlayers()) {
        const cooldownValue = scoreboardCooldown.getScore(player);
        let cooldown = displayMap[cooldownValue] ?? "";
        if (cooldownValue > 0) {
            player.onScreenDisplay.setTitle('', { stayDuration: 2, fadeInDuration: 0, fadeOutDuration: 2, subtitle: '', });
            if (cooldownValue > 1) {
                player.onScreenDisplay.updateSubtitle(cooldown);
                if (cooldownValue > 3) {
                    player.addEffect("weakness", 2, { amplifier: 0, showParticles: false });
                    if (cooldownValue >= 9) {
                        player.addEffect("weakness", 2, { amplifier: 1, showParticles: false });
                        if (cooldownValue >= 15) {
                            player.addEffect("weakness", 2, { amplifier: 2, showParticles: false }); player.addEffect("mining_fatigue", 1, { amplifier: 2, showParticles: false });
                            if (cooldownValue >= 21) { player.addEffect("weakness", 2, { amplifier: 3, showParticles: false }); }
                        }
                    }
                }
            } else if (cooldownValue === 1) { player.onScreenDisplay.setTitle('', { stayDuration: 4, fadeInDuration: 0, fadeOutDuration: 2, subtitle: '', }); }
            scoreboardCooldown.addScore(player, -1);
        }
    }
});

system.runInterval(() => {
    for (const player of world.getPlayers()) {
        const twoHandedMap = ["_battleaxe", "_claymore", "_greatsword", "_hammer", "_katana", "_lance", "_longsword", "_morningstar", "_scythe"];
        const twoHandedDynamic = "hax:two_handed_weapon";
        const playerEquipment = getEquipment(player);
        const mainHandItem = playerEquipment?.getEquipment(EquipmentSlot.Mainhand);
        if (mainHandItem) {
            if (twoHandedMap.some(typeId => mainHandItem.typeId.includes(typeId)) && (twoHandedDynamic !== 1)) { player.setDynamicProperty(twoHandedDynamic, 1); }
            if (!twoHandedMap.some(typeId => mainHandItem.typeId.includes(typeId)) && (twoHandedDynamic !== 0)) { player.setDynamicProperty(twoHandedDynamic, 0); }
        } else if ((twoHandedDynamic === undefined) || (twoHandedDynamic !== 0)) { player.setDynamicProperty(twoHandedDynamic, 0); }
    }
}, 1);

system.runInterval(() => {
    for (const player of world.getPlayers()) {
        const offhandFullMap = ["shield", "rocket", "arrow", "totem", "map", "nautilus_shell"];
        const offhandFullDynamic = "hax:offhand_full";
        const playerEquipment = getEquipment(player);
        const offHandItem = playerEquipment?.getEquipment(EquipmentSlot.Offhand);
        if (offHandItem) {
            if (offhandFullMap.some(typeId => offHandItem.typeId.includes(typeId)) && (offhandFullDynamic !== 1)) { player.setDynamicProperty(offhandFullDynamic, 1); }
            if (!offhandFullMap.some(typeId => offHandItem.typeId.includes(typeId)) && (offhandFullDynamic !== 0)) { player.setDynamicProperty(offhandFullDynamic, 0); }
        } else if ((offhandFullDynamic === undefined) || (offhandFullDynamic !== 0)) { player.setDynamicProperty(offhandFullDynamic, 0); }
    }
}, 4);

system.runInterval(() => {
    for (const player of world.getPlayers()) {
        const twoHandedValue = player?.getDynamicProperty('hax:two_handed_weapon');
        const offhandFullValue = player?.getDynamicProperty('hax:offhand_full');
        if ((twoHandedValue === 1) && (offhandFullValue === 1) && !player.getEffect('weakness')) {
            player.addEffect("weakness", 8, { amplifier: 0, showParticles: false })
        }
    }
});

const parryTest = ["_claymore", "_greatsword", "_hammer", "_katana", "_lance", "_longsword", "_rapier", "_sai"];
system.runInterval(() => {
    for (const player of world.getPlayers()) {
        const playerEquipment = getEquipment(player);
        const mainHandItem = playerEquipment?.getEquipment(EquipmentSlot.Mainhand);
        const parryCooldownValue = scoreboardParryCooldown.getScore(player)
        const isParryOverOrUndefined = parryCooldownValue === undefined || parryCooldownValue <= -20;

        const playerViewDirection = player.getViewDirection();
        const playerDirectionX = playerViewDirection.x;
        const playerDirectionZ = playerViewDirection.z;
        if (mainHandItem) {
            if (parryTest.some(typeId => mainHandItem.typeId.includes(typeId)) && player.isSneaking && isParryOverOrUndefined) {
                scoreboardParryCooldown.setScore(player, 7);
                player.addEffect("resistance", 6, { amplifier: 2, showParticles: false })
            }
            if (mainHandItem.typeId.includes("shield") && isParryOverOrUndefined && player.isSneaking) {
                scoreboardParryCooldown.setScore(player, 40);
                player.applyKnockback(playerDirectionX, playerDirectionZ, 4.2, 0.1)
            }
        }
        if (parryCooldownValue > -20) { scoreboardParryCooldown.addScore(player, -1); } else if (parryCooldownValue <= -20) { scoreboardParryCooldown.removeParticipant(player) }
    }
});

world.afterEvents.entityHurt.subscribe(({ damage, damageSource, hurtEntity }) => {
    const damageCauseTypesFilter = ["entityAttack", "entityExplosion", "fall", "fireworks", "flyIntoWall", "projectile", "ramAttack"]
    const parryCooldownValue = scoreboardParryCooldown.getScore(hurtEntity)
    if (12 > parryCooldownValue > 0) {
        if (damageCauseTypesFilter.some((damageType) => damageSource.cause == damageType)) {
            scoreboardParryCooldown.removeParticipant(hurtEntity);
            hurtEntity.addEffect("strength", 14, { amplifier: 0, showParticles: false })
            hurtEntity.playSound("block.bell.hit", { location: hurtEntity.location, pitch: 3.8, volume: 0.2 })
            hurtEntity.runCommand(`execute as @s anchored eyes run particle minecraft: critical_hit_emitter`);
        }
    }
}, { entityTypes: ["minecraft:player"] });
