import { world, system, MolangVariableMap } from "@minecraft/server";
import { Vec3, calculateHitLocation, isInBoundaries } from "../ProjectileHelpers.js";


export const projectileId = "ttav:heat_shell_ifv_projectile";
export const projectileCache = {};
export const dimensions = {};
export const dimensionHeightRanges = {};

export function shootProjectile(player, direction, location, spreadValue = 0) {
    if (spreadValue > 0) {
        direction.x += (Math.random() - 0.5) * (spreadValue * 0.01);
        direction.y += (Math.random() - 0.5) * (spreadValue * 0.01);
        direction.z += (Math.random() - 0.5) * (spreadValue * 0.01);
        direction = Vec3.from(direction).normalize();
    }

    const dimId = player.dimension.id;
    if (!dimensions[dimId]) {
        dimensions[dimId] = player.dimension;
    }

    let e = player.dimension.spawnEntity(projectileId, location);

    const tankTag = Array.from(player.getTags()).find(tag => tag.startsWith('tank_'));
    const tankId = tankTag?.split('_')[1];

    projectileCache[e.id] = {
        speed: 7,
        damage: 6,
        previousLocation: location,
        direction: { ...direction },
        shooter: player.id,
        shooterVehicle: tankId,
        removed: false,
    };

    const velocity = player.getVelocity();
    const impulse = {
        x: direction.x * 7 + velocity.x,
        y: direction.y * 7 + velocity.y,
        z: direction.z * 7 + velocity.z,
    };
    e.applyImpulse(impulse);

    return e;
}

function doSubStepRay(e, projectile, dimension) {
    const totalDelta = Vec3.from(e.location).subtract(projectile.previousLocation);
    const totalDist = totalDelta.length();
    if (totalDist <= 0.0001) {
        return true;
    }

    const steps = 4;
    let currentPos = Vec3.from(projectile.previousLocation);
    let distRemaining = totalDist;
    let dir = totalDelta.normalize();

    for (let i = 0; i < steps; i++) {
        if (projectile.removed) {
            return false;
        }
        const stepDist = distRemaining / (steps - i);
        const nextPos = currentPos.add(dir.multiply(stepDist));

        try {
            const hits = dimension.getEntitiesFromRay(currentPos, dir, {
                maxDistance: Math.ceil(stepDist + 2),
            });
            for (const hit of hits) {
                if (hit.entity.id === projectile.shooter || 
                    hit.entity.id === e.id || 
                    hit.entity.id === projectile.shooterVehicle ||
                    hit.entity.typeId === projectileId) {
                    continue;
                }

                const hasTankRiderTag = Array.from(hit.entity.getTags()).some(tag => tag.startsWith('rider_'));
                if (hasTankRiderTag) {
                    continue;
                }

                const hitLocation = calculateHitLocation(e.location, dir, currentPos, 1);
                onHit(e, projectile, hit.entity, hitLocation);
                return false;
            }
        } catch (err) {}

        try {
            if (projectile.removed) break;
            const blockHit = dimension.getBlockFromRay(currentPos, dir, {
                maxDistance: Math.ceil(stepDist + 2),
                includePassableBlocks: false,
                includeLiquidBlocks: false,
            });
            if (blockHit) {
                const finalLoc = blockHit.location ?? e.location;
                const hitLocation = calculateHitLocation(finalLoc, dir, currentPos, 1);
                onHit(e, projectile, blockHit, hitLocation);
                return false;
            }
        } catch (err) {}

        currentPos = nextPos;
        distRemaining -= stepDist;
    }

    if (!projectile.removed) {
        projectile.previousLocation = e.location;
    }
    return !projectile.removed;
}

export function handleHit(e, projectile) {
    if (!projectile) return false;
    if (projectile.removed) return false;
    if (!isInBoundaries(e)) return false;

    const dimension = e.dimension;
    const subStepResult = doSubStepRay(e, projectile, dimension);
    if (!subStepResult || projectile.removed) {
        return false;
    }

    const delta = Vec3.from(e.location).subtract(projectile.previousLocation);
    if (delta.isZero()) {
        return true;
    }
    const direction = Vec3.from(projectile.direction);
    const length = delta.length();
    const radius = 2.0;
    const location = projectile.previousLocation;

    try {
        const hits = dimension.getEntitiesFromRay(location, direction, {
            maxDistance: Math.ceil(length + 2),
        });
        for (const hit of hits) {
            if (hit.entity.id === projectile.shooter || 
                hit.entity.id === e.id || 
                hit.entity.id === projectile.shooterVehicle ||
                hit.entity.typeId === projectileId) {
                continue;
            }

            const hasTankRiderTag = Array.from(hit.entity.getTags()).some(tag => tag.startsWith('rider_'));
            if (hasTankRiderTag) {
                continue;
            }

            const hitLocation = calculateHitLocation(e.location, direction, location, radius);
            onHit(e, projectile, hit.entity, hitLocation);
            return false;
        }
        } catch (err) {}

    try {
        const blockHit = dimension.getBlockFromRay(location, direction, {
            maxDistance: Math.ceil(length + 2),
            includePassableBlocks: false,
            includeLiquidBlocks: false,
        });
        if (blockHit) {
            const finalLoc = blockHit.location ?? e.location;
            const hitLocation = calculateHitLocation(finalLoc, direction, location, radius);
            onHit(e, projectile, blockHit, hitLocation);
            return false;
        }
    } catch (err) {}

    if (!projectile.removed) {
        projectile.previousLocation = e.location;
    }
    return true;
}
function onHit(e, projectile, target, hitLocation) {
    try {
        e.dimension.spawnParticle("ttx:1_1_s", hitLocation, new MolangVariableMap());
        e.dimension.spawnParticle("ttx:1_2_s", hitLocation, new MolangVariableMap());
        e.dimension.spawnParticle("ttx:1_3_s", hitLocation, new MolangVariableMap());
        e.dimension.spawnParticle("ttav:very_small_shell_aftersmoke_small", hitLocation, new MolangVariableMap());
    } catch (err) {}

    if (target.applyDamage) {
        try {
            target.applyDamage(projectile.damage, {
                cause: "entityAttack",
                damagingEntity: e
            });
        } catch (err) {}
    }

    doExplosionEffect(e.dimension, hitLocation, e);

    projectile.removed = true;
    try {
        e.remove();
    } catch (err) {}
}
function doExplosionEffect(dimension, center, projectileEntity) {
    try {
        const nearbyEntities = dimension.getEntities({
            location: center,
            maxDistance: 4
        });
        dimension.createExplosion(center, 2, {
            breaksBlocks: true,
            causesFire: false,
            allowUnderwater: true
        });
        for (const entity of nearbyEntities) {
            if (entity.typeId === projectileId) {
                continue;
            }
            try {
                entity.applyDamage(10, {
                    cause: "entityAttack",
                    damagingEntity: projectileEntity
                });
            } catch (err) {
            }
        }

    } catch (err) {}
}
