import { system, ItemUseAfterEvent, EntityDamageCause, InputPermissionCategory, ItemComponentTypes, GameMode, EntityComponentTypes, EnchantmentType } from "@minecraft/server"
import { distance, sendDebugMessage, vectorMultiplyByNumber } from "../JM_Utils"

export function bossAxeWeapon(item, player) {

    player.playAnimation('animation.lb_as.boss_axe_weapon.third_person.attack')
    
    player.inputPermissions.setPermissionCategory(InputPermissionCategory.Movement, false)
    player.inputPermissions.setPermissionCategory(InputPermissionCategory.Camera, false)

    const slot = player.selectedSlotIndex

    //impact time
    system.runTimeout(() => {
        const viewDirection = player.getViewDirection()
        viewDirection.y = 0
        const groundExplosionLoc = locateToWorldOffSet(player.location, viewDirection, { x: 2, y: 0.0, z: 0.1 })

        player.dimension.spawnEntity('lb_as:ground_explosion', groundExplosionLoc)
        player.dimension.playSound('random.explode', player.location)
        player.runCommand('/camerashake add @s 2 0.25 positional')

        const damageEntities = player.dimension.getEntities({ location: groundExplosionLoc, maxDistance: 7, excludeTypes: ["lb_as:ground_explosion", "minecraft:item", "minecraft:xp_orb", "minecraft:arrow"] })
        const playerIndex = damageEntities.findIndex(element => element.id == player.id)
        damageEntities.splice(playerIndex, 1)
        if (damageEntities.length > 0) { // only damage axe if something is hit by the attack
            const damageComponent = item.getComponent(ItemComponentTypes.Durability)
            if (player.getGameMode() !== GameMode.Creative) {
                const enchantComponent = item.getComponent(ItemComponentTypes.Enchantable)
                const unbreaking = enchantComponent?.getEnchantment("unbreaking")?.level
                let curDamage = damageComponent.damage
                if (unbreaking > 0) {
                    const chance = 1 / (unbreaking + 1)
                    const roll = Math.random()
                    sendDebugMessage(`yes unbreaking, chance: ${chance}, roll: ${roll}, (${roll < chance ? "damage" : "not damage"})`)
                    if (roll < chance) curDamage++
                } else {
                    // no unbreaking
                    curDamage++
                }
                if (curDamage > damageComponent.damage) {
                    if (curDamage > damageComponent.maxDurability) {
                        item = undefined
                        player.dimension.playSound("random.break", player.location)
                    } else damageComponent.damage = curDamage
                    player.getComponent(EntityComponentTypes.Inventory).container.setItem(slot, item)
                }
            }
        }
        damageEntities.forEach(damageEntity => {
            const dist = distance(damageEntity.location, groundExplosionLoc)
            const damage = equation(dist, 8, 3)
            const directionVect = vectorMultiplyByNumber({
                x: (damageEntity.location.x - groundExplosionLoc.x)/dist,
                z: (damageEntity.location.z - groundExplosionLoc.z)/dist
            }, equation(dist, 3, 3))
            damageEntity.applyDamage(damage, { cause: EntityDamageCause.entityAttack, damagingEntity: player })
            try {
                damageEntity.applyKnockback(directionVect, equation(dist, 0.5, 3))
            } catch (error) {
                if (error.name === "UnsupportedFunctionalityError") return // ignore
                sendDebugMessage(error)
            }
        })
    }, 6)

    function equation(X = 0, A = 1, O = 1) {
        return A * Math.exp(-(Math.pow(X, 2)/(2 * Math.pow(O, 2))))
    }

    system.runTimeout(() => {
        player.inputPermissions.setPermissionCategory(InputPermissionCategory.Movement, true)
        player.inputPermissions.setPermissionCategory(InputPermissionCategory.Camera, true)
    }, 12)
}

function locateToWorldOffSet(jogador, direcaoCamera, offset) {
    // Extrair as propriedades do jogador, direcaoCamera e offset
    const { x: xJogador, y: yJogador, z: zJogador } = jogador;
    const { x: dirX, y: dirY, z: dirZ } = direcaoCamera;
    const { x: offsetX, y: offsetY, z: offsetZ } = offset;

    // Normalizar o vetor de direção (torná-lo unitário)
    const comprimento = Math.sqrt(dirX * dirX + dirY * dirY + dirZ * dirZ);
    const dirXNorm = dirX / comprimento;
    const dirYNorm = dirY / comprimento;
    const dirZNorm = dirZ / comprimento;

    // Calcular a nova posição com base no offset
    const novaX = xJogador + (offsetX * dirXNorm - offsetZ * dirZNorm);
    const novaY = yJogador + offsetY * dirYNorm;
    const novaZ = zJogador + (offsetX * dirZNorm + offsetZ * dirXNorm);
    //z = direita/esquerda, x = frente/trás
    return { x: novaX, y: novaY, z: novaZ }
}