import { world, system, Player, EntityDamageCause, MolangVariableMap } from "@minecraft/server";
import * as attackUtils from "../attackUtils.js";

function applyForce(rift, owner, pulledEntity, targetLocation, range, damageRange = 0) {
    const currentLoc = pulledEntity.location;
    const dx = targetLocation.x - currentLoc.x;
    const dy = targetLocation.y - currentLoc.y;
    const dz = targetLocation.z - currentLoc.z;
    const distance = Math.sqrt(dx * dx + dy * dy + dz * dz) || 0.001;

    // Normalize the direction
    let dirX = dx / distance;
    let dirY = dy / distance;
    let dirZ = dz / distance;

    const clampedDistance = Math.min(distance, range);
    const pullStrength = 1 - (clampedDistance / range); // Closer = stronger

    // Impulse Forces
    const impulseMultiplier = 0.2;
    // const pullStrength = Math.min(distance / range + 0.5, 1);
    const forceX = dirX * pullStrength * impulseMultiplier;
    const forceY = dirY * pullStrength * impulseMultiplier;
    const forceZ = dirZ * pullStrength * impulseMultiplier;

    // Knockback Forces
    const knockMultiplier = 0.5;
    const knockForceX = pullStrength * knockMultiplier;
    const knockForceY = dirY * pullStrength * knockMultiplier;

    if (pulledEntity instanceof Player) {
        pulledEntity.applyKnockback(dirX, dirZ, knockForceX, knockForceY);
    } else {
        pulledEntity.applyImpulse({ x: forceX, y: forceY, z: forceZ });
    }

    if (damageRange && distance < damageRange) {
        const damage = 1;
        attackUtils.applyModifiedDamage(pulledEntity, damage, { cause: EntityDamageCause.magic, damagingEntity: owner });
    }
}

function knockEntities(rift, owner, entity, targetLocation, range, explodeDamage = 5) {
    const currentLoc = entity.location;
    const dx = targetLocation.x - currentLoc.x;
    const dy = targetLocation.y - currentLoc.y;
    const dz = targetLocation.z - currentLoc.z;
    const distance = Math.sqrt(dx * dx + dy * dy + dz * dz) || 0.001;

    // Normalize direction away from the target location
    const dirX = -(dx / distance);
    const dirY = -(dy / distance);
    const dirZ = -(dz / distance);

    // Knockback strength is strongest when close to center
    const clampedDistance = Math.min(distance, range);
    const pushStrength = 1 - (clampedDistance / range); // Closer = stronger

    const knockMultiplier = 2;
    const knockForceX = pushStrength * knockMultiplier;
    const knockForceY = Math.max(0.4, dirY * pushStrength * knockMultiplier); // Add minimum upward lift


    attackUtils.applyModifiedDamage(entity, explodeDamage, { cause: EntityDamageCause.entityExplosion, damagingEntity: owner });
    entity.applyKnockback(dirX, dirZ, knockForceX, knockForceY);
}

export function riftTick(rift) {
    if (!rift.isValid()) {
        rift?.remove();
        return;
    }

    const timeCreated = rift.getDynamicProperty("oct_eg:time_created");
    const lifeTime = rift.getDynamicProperty("oct_eg:life_time");
    const owner = rift.getComponent("minecraft:tameable")?.tamedToPlayer;

    const range = rift.getDynamicProperty("oct_eg:pull_range");
    const sound_key = rift.getDynamicProperty("oct_eg:sound_key");
    const shouldApplyTickDamage = rift.getDynamicProperty("oct_eg:apply_tick_damage");
    const damage = rift.getDynamicProperty("oct_eg:damage");


    if (!rift.getDynamicProperty("oct_eg:initialized")) { // Initialization logic
        const molang = new MolangVariableMap();
        molang.setFloat('life', lifeTime);

        rift.dimension.spawnParticle("oct_eg:ender_rift.rift", rift.location, molang);
        rift.dimension.spawnParticle("oct_eg:ender_rift.smoke", rift.location);

        rift.runCommand(`/camerashake add @a[r=20] 0.2 1 positional`);

        if (sound_key === "upgraded") {
            rift.dimension.playSound("octovon.eg.gauntlet.ender.ender_rift.spawn_long", rift.location);
        } else {
            rift.dimension.playSound("octovon.eg.gauntlet.ender.ender_rift.spawn_short", rift.location);
        }

        rift.setDynamicProperty("oct_eg:initialized", true);
    }

    if (system.currentTick % 10 === 0
        && system.currentTick !== timeCreated
        && system.currentTick - timeCreated < (lifeTime - 1) * 20
    ) {
        rift.dimension.spawnParticle("oct_eg:ender_rift.smoke", rift.location);
        rift.runCommand(`/camerashake add @a[r=15] 0.1 1 positional`);
    };

    const pullRange = range;
    const explodeRange = 3;
    const damageRange = shouldApplyTickDamage ? 1 : 0;
    const explodeDamage = damage;

    const location = {
        x: rift.location.x,
        y: rift.location.y + 1,
        z: rift.location.z
    };

    if (system.currentTick - timeCreated >= lifeTime * 20) { // Explode when time is up
        for (const entity of attackUtils.getValidTargetsInRange(rift,
            {
                startLocation: location,
                maxDistance: explodeRange,
                excludedEntities: [owner]
            })
        ) {
            knockEntities(rift, owner, entity, location, explodeRange, explodeDamage);
        }

        rift.dimension.spawnParticle("oct_eg:ender_rift.explode", rift.location);
        rift.dimension.playSound("octovon.eg.gauntlet.ender.ender_rift.explode", rift.location);

        rift.runCommand(`/camerashake add @a[r=10] 0.6 0.2 positional`);

        rift.remove();
        return;

    } else { // Pull targets
        for (const entity of attackUtils.getValidTargetsInRange(rift,
            {
                startLocation: location,
                maxDistance: pullRange,
                excludedEntities: [owner]
            }).filter(entity => attackUtils.isValidPullTarget(entity))
        ) {
            applyForce(rift, owner, entity, location, pullRange, damageRange);
        }
    }
}