import { describe, it, expect } from 'bun:test';
import { World } from '@common/rpg/core/world';
import { Cooldown } from '@common/rpg/components/cooldown';
import { CooldownSystem } from '@common/rpg/systems/cooldown';

function world() {
    const w = new World();
    w.addSystem(new CooldownSystem());
    return w;
}

describe('Cooldown — initial state', () => {
    it('starts not ready', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(5));
        expect(e.get(Cooldown)!.ready).toBeFalse();
    });

    it('remaining equals duration on creation', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(3));
        const cd = e.get(Cooldown)!;
        expect(cd.state.remaining).toBe(3);
        expect(cd.state.duration).toBe(3);
    });
});

describe('Cooldown — clear()', () => {
    it('marks as ready immediately', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(10));
        const cd = e.get(Cooldown)!;
        cd.clear();
        expect(cd.ready).toBeTrue();
    });

    it("emits 'ready' event", () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(10));
        const events: unknown[] = [];
        e.on('Cooldown.ready', () => events.push(true));
        e.get(Cooldown)!.clear();
        expect(events.length).toBe(1);
    });

    it('is no-op when already ready', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(1));
        const cd = e.get(Cooldown)!;
        cd.clear();
        const events: unknown[] = [];
        e.on('Cooldown.ready', () => events.push(true));
        cd.clear();
        expect(events.length).toBe(0);
    });
});

describe('Cooldown — reset()', () => {
    it('resets remaining to duration', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(5));
        const cd = e.get(Cooldown)!;
        cd.clear();
        cd.reset();
        expect(cd.ready).toBeFalse();
        expect(cd.state.remaining).toBe(5);
    });

    it('reset(duration) changes duration and remaining', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(5));
        const cd = e.get(Cooldown)!;
        cd.reset(10);
        expect(cd.state.duration).toBe(10);
        expect(cd.state.remaining).toBe(10);
    });
});

describe('Cooldown — update(dt)', () => {
    it('counts down remaining', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(5));
        const cd = e.get(Cooldown)!;
        cd.update(2);
        expect(cd.state.remaining).toBe(3);
        expect(cd.ready).toBeFalse();
    });

    it('becomes ready when remaining reaches zero', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(3));
        const cd = e.get(Cooldown)!;
        cd.update(3);
        expect(cd.ready).toBeTrue();
    });

    it("emits 'ready' when countdown completes", () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(2));
        const events: unknown[] = [];
        e.on('Cooldown.ready', () => events.push(true));
        e.get(Cooldown)!.update(2);
        expect(events.length).toBe(1);
    });

    it('does not go below zero', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(1));
        const cd = e.get(Cooldown)!;
        cd.update(100);
        expect(cd.state.remaining).toBe(0);
    });

    it('is no-op when already ready', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(1));
        const cd = e.get(Cooldown)!;
        cd.clear();
        const events: unknown[] = [];
        e.on('Cooldown.ready', () => events.push(true));
        cd.update(1);
        expect(events.length).toBe(0);
    });
});

describe('CooldownSystem', () => {
    it('drives all cooldowns each tick', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(2));
        w.update(1);
        expect(e.get(Cooldown)!.state.remaining).toBe(1);
    });

    it('marks cooldown ready after enough ticks', () => {
        const w = world();
        const e = w.createEntity();
        e.add(new Cooldown(2));
        const events: unknown[] = [];
        e.on('Cooldown.ready', () => events.push(true));
        w.update(1);
        w.update(1);
        expect(events.length).toBe(1);
        expect(e.get(Cooldown)!.ready).toBeTrue();
    });

    it('handles multiple cooldowns on different entities', () => {
        const w = world();
        const a = w.createEntity('a');
        const b = w.createEntity('b');
        a.add(new Cooldown(1));
        b.add(new Cooldown(3));
        w.update(2);
        expect(a.get(Cooldown)!.ready).toBeTrue();
        expect(b.get(Cooldown)!.ready).toBeFalse();
    });
});
