import { describe, it, expect, beforeEach } from 'vitest';
import { traceProjectile } from '../CombatLogic';
import type { World } from '../../../core/types';
import { EntityManager } from '../../EntityManager';
import { TileType } from '../../../core/terrain';

describe('CombatLogic', () => {
    // Mock World
    const mockWorld: World = {
        width: 10,
        height: 10,
        tiles: new Array(100).fill(TileType.EMPTY),
        actors: new Map(),
        exit: { x: 9, y: 9 }
    };

    // Helper to set wall
    const setWall = (x: number, y: number) => {
        mockWorld.tiles[y * mockWorld.width + x] = TileType.WALL;
    };
    
    // Helper to clear world
    const clearWorld = () => {
        mockWorld.tiles.fill(TileType.EMPTY);
        mockWorld.actors.clear();
    };

    // Mock EntityManager
    const mockEntityManager = {
        getActorsAt: (x: number, y: number) => {
            return [...mockWorld.actors.values()].filter(a => a.pos.x === x && a.pos.y === y);
        }
    } as unknown as EntityManager;

    beforeEach(() => {
        clearWorld();
    });

    describe('traceProjectile', () => {
        it('should travel full path if no obstacles', () => {
            const start = { x: 0, y: 0 };
            const end = { x: 5, y: 0 };
            
            const result = traceProjectile(mockWorld, start, end, mockEntityManager);
            
            expect(result.blockedPos).toEqual(end);
            expect(result.hitActorId).toBeUndefined();
            // Path should be (0,0) -> (1,0) -> (2,0) -> (3,0) -> (4,0) -> (5,0)
            // But raycast implementation includes start? 
            // CombatLogic logic: "skip start" -> loop i=1
            // So result.path is full array from raycast.
            expect(result.path).toHaveLength(6); 
        });

        it('should stop at wall', () => {
            const start = { x: 0, y: 0 };
            const end = { x: 5, y: 0 };
            setWall(3, 0); // Wall at (3,0)

            const result = traceProjectile(mockWorld, start, end, mockEntityManager);
            
            expect(result.blockedPos).toEqual({ x: 3, y: 0 });
            expect(result.hitActorId).toBeUndefined();
        });

        it('should stop at enemy', () => {
            const start = { x: 0, y: 0 };
            const end = { x: 5, y: 0 };
            
            // Place enemy at (3,0)
            const enemyId = 2;
            mockWorld.actors.set(enemyId, {
                id: enemyId,
                type: 'rat',
                category: 'combatant',
                pos: { x: 3, y: 0 },
                isPlayer: false
                // ... other props mocked if needed
            } as any);

            const result = traceProjectile(mockWorld, start, end, mockEntityManager, 1); // Shooter 1
            
            expect(result.blockedPos).toEqual({ x: 3, y: 0 });
            expect(result.hitActorId).toBe(enemyId);
        });

        it('should ignore shooter position', () => {
             const start = { x: 0, y: 0 };
             const end = { x: 5, y: 0 };
             
             // Shooter at start
             mockWorld.actors.set(1, {
                 id: 1,
                 type: 'player',
                 category: 'combatant',
                 pos: { x: 0, y: 0 },
                 isPlayer: true
             } as any);

             const result = traceProjectile(mockWorld, start, end, mockEntityManager, 1);
             
             // Should not hit self
             expect(result.hitActorId).toBeUndefined();
             expect(result.blockedPos).toEqual(end);
        });

        it('should ignore non-combatant actors (e.g. items)', () => {
            const start = { x: 0, y: 0 };
            const end = { x: 5, y: 0 };
            
            // Item at (3,0)
            mockWorld.actors.set(99, {
                id: 99,
                category: 'item_drop',
                pos: { x: 3, y: 0 },
            } as any);

            const result = traceProjectile(mockWorld, start, end, mockEntityManager);
            
            // Should pass through item
            expect(result.blockedPos).toEqual(end);
            expect(result.hitActorId).toBeUndefined();
       });
    });
});
