import { ChiRenAI, EnemyManager, HeiRenAI, QingRenAI, YaoFangAI } from '@logic/EnemyAI';
import { EnemyType, IEnemyConfig } from '@data/Interfaces';

function cfg(id: EnemyType, intervalSec: number, speed = 0): IEnemyConfig {
    return {
        id,
        displayName: id,
        size: { w: 16, h: 16 },
        moveSpeed: speed,
        attackIntervalSec: intervalSec,
        attacks: ['shuriken'],
        hp: 1,
    };
}

const idlePlayer = { x: 300, y: 16, isGrounded: true };

describe('QingRenAI (req 6.1)', () => {
    it('throws a shuriken at the player when out of melee range', () => {
        const ai = new QingRenAI(cfg(EnemyType.QingRen, 2.0), 0, 16);
        const actions = ai.update({ dtSec: 2.0, nowMs: 0, player: idlePlayer });
        expect(actions[0].kind).toBe('fire_bullet');
        expect(actions[0].attackType).toBe('shuriken');
        expect(actions[0].velX).toBeGreaterThan(0);
    });

    it('melee swings when player is close', () => {
        const ai = new QingRenAI(cfg(EnemyType.QingRen, 2.0), 310, 16);
        const actions = ai.update({ dtSec: 2.0, nowMs: 0, player: idlePlayer });
        expect(actions[0].kind).toBe('melee_swing');
    });

    it('respects attack interval — no burst within one tick', () => {
        const ai = new QingRenAI(cfg(EnemyType.QingRen, 2.0), 0, 16);
        ai.update({ dtSec: 2.0, nowMs: 0, player: idlePlayer });
        const actions = ai.update({ dtSec: 0.1, nowMs: 0, player: idlePlayer });
        expect(actions.length).toBe(0);
    });
});

describe('ChiRenAI (req 6.2-6.3)', () => {
    it('moves horizontally toward the player at 120 px/s', () => {
        const ai = new ChiRenAI(cfg(EnemyType.ChiRen, 1.5, 120), 0, 16);
        ai.update({ dtSec: 1, nowMs: 0, player: idlePlayer });
        expect(ai.pos.x).toBeGreaterThan(0);
    });

    it('throws smoke bombs at the configured cadence', () => {
        const ai = new ChiRenAI(cfg(EnemyType.ChiRen, 1.5, 120), 0, 16);
        const actions = ai.update({ dtSec: 1.5, nowMs: 0, player: idlePlayer });
        expect(actions.some((a) => a.attackType === 'smoke_bomb')).toBe(true);
    });

    it('bumps upward when the player is idle within intercept range', () => {
        const ai = new ChiRenAI(cfg(EnemyType.ChiRen, 1.5, 120), 250, 16);
        const before = ai.pos.y;
        ai.update({ dtSec: 0.016, nowMs: 0, player: idlePlayer });
        expect(ai.pos.y).toBeGreaterThan(before);
    });
});

describe('HeiRenAI (req 6.5)', () => {
    it('drops exactly one magic flute on kill', () => {
        const ai = new HeiRenAI(cfg(EnemyType.HeiRen, 2.5), 100, 16);
        const firstDrop = ai.onKilled();
        const secondDrop = ai.onKilled();
        expect(firstDrop[0].itemId).toBe('mo_di');
        expect(secondDrop.length).toBe(0);
    });
});

describe('YaoFangAI (req 6.6)', () => {
    it('launches straight-line fireballs at 3s cadence', () => {
        const ai = new YaoFangAI(cfg(EnemyType.YaoFang, 3.0), 0, 16);
        const a = ai.update({ dtSec: 3.0, nowMs: 0, player: idlePlayer });
        expect(a[0].attackType).toBe('fireball');
    });
});

describe('EnemyManager culling (req 6.7)', () => {
    it('skips update for enemies outside the camera cull rect', () => {
        const mgr = new EnemyManager();
        const far = new YaoFangAI(cfg(EnemyType.YaoFang, 3.0), 5000, 16);
        mgr.spawn(far);
        const actions = mgr.update(3.0, 0, idlePlayer, {
            leftX: 0,
            rightX: 960,
            topY: 540,
            bottomY: 0,
        });
        expect(actions.length).toBe(0);
    });

    it('updates enemies inside the cull rect', () => {
        const mgr = new EnemyManager();
        const near = new YaoFangAI(cfg(EnemyType.YaoFang, 3.0), 200, 16);
        mgr.spawn(near);
        const actions = mgr.update(3.0, 0, idlePlayer, {
            leftX: 0,
            rightX: 960,
            topY: 540,
            bottomY: 0,
        });
        expect(actions.length).toBe(1);
        expect(actions[0].kind).toBe('fire_bullet');
    });

    it('kill() converts Hei Ren to a magic-flute drop', () => {
        const mgr = new EnemyManager();
        const hei = new HeiRenAI(cfg(EnemyType.HeiRen, 2.5), 50, 16);
        mgr.spawn(hei);
        const drops = mgr.kill(hei);
        expect(drops[0].itemId).toBe('mo_di');
        expect(hei.alive).toBe(false);
    });
});
