import { type World, type Vec2, type EntityId } from "../../core/types";
import { isBlocked } from "../world/world-logic";
import { raycast } from "../../core/math";
import { EntityManager } from "../EntityManager";

export interface ProjectileResult {
    path: Vec2[];
    blockedPos: Vec2;
    hitActorId?: EntityId;
}

/**
 * Calculates the path and impact of a projectile.
 */
export function traceProjectile(
    world: World, 
    start: Vec2, 
    target: Vec2, 
    entityManager: EntityManager,
    shooterId?: EntityId
): ProjectileResult {
    const points = raycast(start.x, start.y, target.x, target.y);
    let blockedPos = target;
    let hitActorId: EntityId | undefined;

    // Iterate points (skip start)
    for (let i = 1; i < points.length; i++) {
        const p = points[i];

        // Check for blocking
        if (isBlocked(world, p.x, p.y, entityManager)) {
            // Check if we hit a combatant
            const actors = entityManager.getActorsAt(p.x, p.y);
            const enemy = actors.find(a => a.category === "combatant" && a.id !== shooterId);
            
            if (enemy) {
                hitActorId = enemy.id;
                blockedPos = p;
            } else {
                // Hit wall or other obstacle
                blockedPos = p;
            }
            break;
        }
        blockedPos = p;
    }

    return {
        path: points,
        blockedPos,
        hitActorId
    };
}

/**
 * Finds the closest visible enemy to a given position.
 */
export function getClosestVisibleEnemy(
    world: World,
    origin: Vec2,
    seenTiles: Set<string> | boolean[] | Uint8Array, // Support various visibility structures
    width?: number // Required if seenTiles is a flat array
): Vec2 | null {
    let closestDistSq = Infinity;
    let closestPos: Vec2 | null = null;
    
    // Helper to check visibility
    const isVisible = (x: number, y: number) => {
        if (Array.isArray(seenTiles) || seenTiles instanceof Uint8Array || seenTiles instanceof Int8Array) {
            // Flat array
            if (!width) return false;
            return (seenTiles as any)[y * width + x]; 
        } else {
            // Set<string>
            return (seenTiles as Set<string>).has(`${x},${y}`);
        }
    };

    for (const actor of world.actors.values()) {
        if (actor.category !== "combatant" || actor.isPlayer) continue;
        
        // Check visibility
        if (!isVisible(actor.pos.x, actor.pos.y)) continue;

        const dx = actor.pos.x - origin.x;
        const dy = actor.pos.y - origin.y;
        const distSq = dx*dx + dy*dy;

        if (distSq < closestDistSq) {
            closestDistSq = distSq;
            closestPos = { x: actor.pos.x, y: actor.pos.y };
        }
    }

    return closestPos;
}
