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

export const flareProjectileId = "ttav:bullet_projectile";
export const flareProjectileCache = {};
export const dimensions = {};
const cooldowns = new Map();
const COOLDOWN_TICKS = 40; 

function shootFlare(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 flare = player.dimension.spawnEntity(flareProjectileId, location);

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

    flareProjectileCache[flare.id] = {
        speed: 5,
        previousLocation: location,
        direction: { ...direction },
        shooter: player.id,
        shooterVehicle: tankId,
        removed: false
    };

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

    return flare;
}
function doSubStepRay(flare, projectile, dimension) {
    if (!flare?.location || !projectile?.previousLocation) return false;

    const totalDelta = Vec3.from(flare.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();
    let particleCount = 0;

    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 || 
                    hit.entity.id === projectile.shooter || 
                    hit.entity.id === flare.id || 
                    hit.entity.id === projectile.shooterVehicle ||
                    hit.entity.typeId === flareProjectileId) {
                    continue;
                }

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

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

        currentPos = nextPos;
        distRemaining -= stepDist;

        if (!projectile.removed && flare?.location && particleCount < 1) {
            flare.dimension.spawnParticle("ttav:flare_shell_impact_smoke_flare", flare.location, new MolangVariableMap());
            particleCount++;
        }
    }

    if (!projectile.removed && flare?.location) {
        projectile.previousLocation = flare.location;
    }
    return !projectile.removed;
}


function onHit(flare, projectile, target, hitLocation) {
    try {
        if (flare?.dimension) {
            flare.dimension.spawnParticle("ttav:flare_shell_impact_smoke_flare", hitLocation, new MolangVariableMap());
        }
    } catch (err) {}

    projectile.removed = true;
    try {
        if (flare?.remove) flare.remove();
    } catch (err) {}
}

function handleFlare(flare, projectile) {
    if (!flare?.id || !flare?.dimension || !projectile) return false;
    if (projectile.removed || !isInBoundaries(flare)) return false;

    const dimension = flare.dimension;
    if (!dimension) return false;

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

    if (flare?.location) {
        projectile.previousLocation = flare.location;
    }
    return true;
}

world.afterEvents.itemUse.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.flareGunItem) {
        const currentAmmo = getFlareAmmo(tank);
        
        if (currentAmmo <= 0) {
            player.onScreenDisplay.setActionBar("§c无信号弹!");
            player.playSound("note.bass", { pitch: 1.0, volume: 1.0 });
            return;
        }
        
        if (reduceFlareAmmo(tank)) {
            const currentTick = system.currentTick;
            const lastFiredTick = cooldowns.get(tank.id) || 0;
            
            if (currentTick - lastFiredTick >= COOLDOWN_TICKS) {
                tank.triggerEvent("flare_gun_animation");
                player.playSound("ttavFlareGunshoot", { pitch: 1.0, volume: 1.0 });
                player.runCommand(`effect @s invisibility 10 1 true`);
                const [location, direction] = calculateShootLocationAndDirection(player);
                shootFlare(player, direction, location, 5);
                
                cooldowns.set(tank.id, currentTick);
            } else {
                const remainingTicks = COOLDOWN_TICKS - (currentTick - lastFiredTick);
                player.onScreenDisplay.setActionBar(`§c装弹中... ${Math.ceil(remainingTicks/20)}s`);
            }
        }
    }
});

system.runInterval(() => {
    const ticked = [];
    for (const dimension of Object.values(dimensions)) {
        if (!dimension) continue;
        
        const allEntities = dimension.getEntities();
        if (!allEntities) continue;

        for (const flare of allEntities) {
            if (!flare?.id || flare.typeId !== flareProjectileId) continue;
            
            ticked.push(flare.id);
            const projectileData = flareProjectileCache[flare.id];
            if (projectileData) {
                handleFlare(flare, projectileData);
            }
        }
    }

    for (const id in flareProjectileCache) {
        if (!ticked.includes(id)) {
            delete flareProjectileCache[id];
        }
    }
}, 1);
