import { world, system, MolangVariableMap } from "@minecraft/server";
import { Vec3, calculateHitLocation, calculateShootLocationAndDirection, getDimension, getDimensionHeightRange, isInBoundaries } from "./ProjectileHelpers.js";
import { VEHICLES } from "./VehiclesRoot.js";
import { reduceSubAmmo, getSubAmmo } from "./AmmoSystem.js";

export const projectileId = "ttav:bullet_projectile";
export const projectileCache = {};
export const dimensions = {};
export const dimensionHeightRanges = {};
let isSubGunFiring = false;
let firingInterval = null;

const FIRING_RATE = 2; 
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: 8,
        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);

    const viewDirection = player.getViewDirection();
    const angle = Math.atan2(viewDirection.z, viewDirection.x);

    const FORWARD_DISTANCE = 2.0;
    const SIDE_DISTANCE = 0.7;
    const HEIGHT_OFFSET = 1.1;
    const HORIZONTAL_ANGLE = 1;
    const VERTICAL_ANGLE = 1;

    const headLoc = player.getHeadLocation();
    
    
    const leftBasePoint = {
        x: headLoc.x,
        y: headLoc.y,
        z: headLoc.z + 2.7
    };

    
    const rightBasePoint = {
        x: headLoc.x,
        y: headLoc.y,
        z: headLoc.z + 2.7
    };

    const forwardX = Math.cos(angle) * FORWARD_DISTANCE;
    const forwardZ = Math.sin(angle) * FORWARD_DISTANCE;

    const sideX = Math.cos(angle + Math.PI/2) * SIDE_DISTANCE;
    const sideZ = Math.sin(angle + Math.PI/2) * SIDE_DISTANCE;

    
    const baseDirection = Vec3.from(viewDirection);
    baseDirection.y -= Math.sin((VERTICAL_ANGLE * Math.PI) / 180);

    
    const leftGunDirection = Vec3.from(baseDirection).rotate(Vec3.Up, -HORIZONTAL_ANGLE);
    const rightGunDirection = Vec3.from(baseDirection).rotate(Vec3.Up, HORIZONTAL_ANGLE);

    
    let molangLeft = new MolangVariableMap();
    molangLeft.setSpeedAndDirection('direction', 20, {
        x: leftGunDirection.x,
        y: leftGunDirection.y,
        z: leftGunDirection.z
    });

    let molangRight = new MolangVariableMap();
    molangRight.setSpeedAndDirection('direction', 20, {
        x: rightGunDirection.x,
        y: rightGunDirection.y,
        z: rightGunDirection.z
    });

    
    player.dimension.spawnParticle('ttav:bullet_trails2',
        { 
            x: leftBasePoint.x + forwardX - sideX,
            y: leftBasePoint.y + HEIGHT_OFFSET,
            z: leftBasePoint.z + forwardZ - sideZ
        },
        molangLeft
    );

    
    player.dimension.spawnParticle('ttav:bullet_trails2',
        { 
            x: rightBasePoint.x + forwardX + sideX,
            y: rightBasePoint.y + HEIGHT_OFFSET,
            z: rightBasePoint.z + forwardZ + sideZ
        },
        molangRight
    );

    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;
}

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("tt:v_m_bullet_sparks", hitLocation, new MolangVariableMap());
        e.dimension.spawnParticle("ttav:projectile_impact_smoke_medium", hitLocation, new MolangVariableMap());
    } catch (err) {}

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

    projectile.removed = true;
    try {
        e.remove();
    } catch (err) {}
}

world.afterEvents.itemStartUse.subscribe((event) => {
    const player = event.source;
    const item = event.itemStack;

    const tankTag = Array.from(player.getTags()).find(tag => tag.startsWith('tank_'));
    if (!tankTag) return;

    const tankId = tankTag.split('_')[1];
    const tank = player.dimension.getEntities().find(e => e.id === tankId);
    if (!tank) return;

    const vehicleConfig = Object.values(VEHICLES).find(v => v.identifier === tank.typeId);
    if (!vehicleConfig) return;
    
    if (!['APV_ROCKET', 'APV'].includes(vehicleConfig.modelType)) return;

    if (item?.typeId === vehicleConfig.subGunItem) {
        if (firingInterval) {
            system.clearRun(firingInterval);
        }

        isSubGunFiring = true;
        firingInterval = system.runInterval(() => {
            if (!isSubGunFiring) {
                system.clearRun(firingInterval);
                firingInterval = null;
                return;
            }

            if (tank && getSubAmmo(tank) > 0) {
                if (reduceSubAmmo(tank)) {
                    tank.playAnimation("animation.apv_rocket107.fire_sub_loop");
                    player.runCommand(`/camerashake add @p[r=4] 0.01 0.01 rotational`);
                    const [location, direction] = calculateShootLocationAndDirection(player);
                    shootProjectile(player, direction, location, 2);
                }
            } else {
                isSubGunFiring = false;
                system.clearRun(firingInterval);
                firingInterval = null;
                player.onScreenDisplay.setActionBar("§c没有弹药!");
                player.playSound("note.bass", { pitch: 1.0, volume: 1.0 });
            }
        }, FIRING_RATE);
    }
});

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

    const tankTag = Array.from(player.getTags()).find(tag => tag.startsWith('tank_'));
    if (!tankTag) return;

    const tankId = tankTag.split('_')[1];
    const tank = player.dimension.getEntities().find(e => e.id === tankId);
    if (!tank) return;

    const vehicleConfig = Object.values(VEHICLES).find(v => v.identifier === tank.typeId);
    if (!vehicleConfig) return;

    if (item?.typeId === vehicleConfig.subGunItem) {
        isSubGunFiring = false;
        if (firingInterval) {
            tank.playAnimation("animation.apv_rocket107.fire_sub_loop_end");
            system.clearRun(firingInterval);
            firingInterval = null;
        }
    }
});


system.runInterval(() => {
    const ticked = [];
    for (const dimension of Object.values(dimensions)) {
        const allEntities = dimension.getEntities();
        for (const e of allEntities) {
            if (e.typeId === projectileId) {
                ticked.push(e.id);
                handleHit(e, projectileCache[e.id]);
            }
        }
    }
    for (const id in projectileCache) {
        if (!ticked.includes(id)) {
            delete projectileCache[id];
        }
    }
}, 1);
