
import { describe, it, expect } from 'vitest';
import { decideEnemyAction, applyAction, stepUntilPlayerTurn } from '../simulation/simulation';
import { type World, type Actor, type EntityId, type CombatantActor } from '../../core/types';
import { EntityManager } from '../EntityManager';
import { TileType } from '../../core/terrain';

const createTestWorld = (actors: Map<EntityId, Actor>): World => {
    return {
        width: 10,
        height: 10,
        tiles: new Array(100).fill(TileType.EMPTY),
        actors,
        exit: { x: 9, y: 9 }
    };
  };
  
const createTestStats = (overrides: Partial<any> = {}) => ({
    maxHp: 20, hp: 20, attack: 5, defense: 2, level: 1, exp: 0, expToNextLevel: 10,
    statPoints: 0, skillPoints: 0, strength: 10, dexterity: 10, intelligence: 10, passiveNodes: [],
    critChance: 0, critMultiplier: 100, accuracy: 100, lifesteal: 0, evasion: 0, blockChance: 0, luck: 0,
    ...overrides
});

describe('AI Behavior & Scheduling', () => {
    let entityManager: EntityManager;

    // -------------------------------------------------------------------------
    // Scheduling Fairness
    // -------------------------------------------------------------------------
    describe('Scheduler Fairness', () => {
        it("should allow slower actors to act eventually", () => {
            const actors = new Map<EntityId, Actor>();
            // Player Speed 100
            const player = { 
                id: 1, category: "combatant", isPlayer: true, pos: { x: 0, y: 0 }, 
                speed: 100, stats: createTestStats(), energy: 0 
            } as any;
            
            // Rat Speed 80 (Slow)
            const rat = { 
                id: 2, category: "combatant", isPlayer: false, pos: { x: 9, y: 9 }, 
                speed: 80, stats: createTestStats(), aiState: "wandering", energy: 0 
            } as any;
            
            actors.set(1, player);
            actors.set(2, rat);
            const world = createTestWorld(actors);
            entityManager = new EntityManager(world);
    
            let ratMoves = 0;
            
            // Simulate 20 player turns
            // With fair scheduling, Rat (80 speed) should move approx 80% as often as Player (100 speed).
            // So in 20 turns, approx 16 moves. Definitley > 0.
            for (let i = 0; i < 20; i++) {
                const result = stepUntilPlayerTurn(world, 1, entityManager);
                const enemyActs = result.events.filter(e => 
                    (e.type === "moved" || e.type === "waited" || e.type === "enemy-alerted") && 
                    ((e as any).actorId === 2 || (e as any).enemyId === 2)
                );
                
                // console.log(`Turn ${i}: Events`, result.events);
                if (enemyActs.length > 0) ratMoves++;
            }
            // console.log(`Total Rat Moves: ${ratMoves}`);
            expect(ratMoves).toBeGreaterThan(0);
        });
    });

    // -------------------------------------------------------------------------
    // Vision & Perception
    // -------------------------------------------------------------------------
    describe('AI Vision', () => {
        const terrainTypes = [
            { type: TileType.EMPTY, name: 'Empty' },
            { type: TileType.GRASS, name: 'Grass' }, // Blocks Vision normally
            { type: TileType.GRASS_SAPLINGS, name: 'Saplings' },
        ];

        terrainTypes.forEach(({ type, name }) => {
            it(`should see player when standing on ${name}`, () => {
                const actors = new Map<EntityId, Actor>();
                 actors.set(1, { id: 1, category: "combatant", isPlayer: true, pos: { x: 5, y: 0 }, stats: createTestStats(), energy: 0 } as any);
                 actors.set(2, { 
                     id: 2, category: "combatant", isPlayer: false, pos: { x: 0, y: 0 }, 
                     stats: createTestStats(), aiState: "wandering", energy: 0 
                 } as any);
                 
                const world = createTestWorld(actors);
                world.tiles[0] = type;
                
                // Rat at 0,0. Player at 5,0.
                decideEnemyAction(world, actors.get(2) as any, actors.get(1) as any, new EntityManager(world));
                
                expect((actors.get(2) as CombatantActor).aiState).toBe("alerted");
            });
        });
    });

    // -------------------------------------------------------------------------
    // Aggression & State Machine
    // -------------------------------------------------------------------------
    describe('AI Aggression State Machine', () => {
        it('should become pursuing when damaged by player, even if not sighting player', () => {
             const actors = new Map<EntityId, Actor>();
             // Player far away/invisible (simulated logic)
             const player = { id: 1, category: "combatant", isPlayer: true, pos: { x: 0, y: 0 }, stats: createTestStats({ attack: 1, accuracy: 100 }), energy: 0 } as any;
             const enemy = { 
                 id: 2, category: "combatant", isPlayer: false, pos: { x: 0, y: 5 }, 
                 stats: createTestStats({ hp: 10, defense: 0, evasion: 0 }), aiState: "wandering", energy: 0 
             } as any;
             
             actors.set(1, player);
             actors.set(2, enemy);
             const world = createTestWorld(actors);
             
             const em = new EntityManager(world);
             applyAction(world, 1, { type: "attack", targetId: 2 }, em);
             
             const updatedEnemy = actors.get(2) as CombatantActor;
             expect(updatedEnemy.aiState).toBe("pursuing");
             expect(updatedEnemy.lastKnownPlayerPos).toEqual(player.pos);
        });

        it("should transition from alerted to pursuing after delay even if sight is blocked", () => {
            const actors = new Map<EntityId, Actor>();
            const player = { id: 1, category: "combatant", isPlayer: true, pos: { x: 9, y: 9 }, stats: createTestStats(), energy: 0 } as any;
            const enemy = { 
                id: 2, 
                category: "combatant", 
                isPlayer: false, 
                pos: { x: 0, y: 0 }, 
                stats: createTestStats(),
                aiState: "alerted",
                alertedAt: Date.now() - 2000, // Alerted 2 seconds ago
                lastKnownPlayerPos: { x: 9, y: 9 }, // Known position
                energy: 0
            } as any;
            
            actors.set(1, player);
            actors.set(2, enemy);
            const world = createTestWorld(actors);
            
            // Player is far away and potentially blocked
            world.tiles[1] = TileType.WALL; // x=1, y=0 blocked
            
            decideEnemyAction(world, enemy, player, new EntityManager(world));
            
            // alerted -> pursuing (due to time) -> searching (due to no sight)
            expect(enemy.aiState).toBe("searching"); 
        });
    });
});
