import { describe, it, expect } from 'bun:test';
import { World, Component, COMPONENT_KEY } from '@common/rpg/core/world';
import { component } from '@common/rpg/utils/decorators';
import { Serialization } from '@common/rpg/core/serialization';
import { Stat } from '@common/rpg/components/stat';
import { Effect } from '@common/rpg/components/effect';
import { EffectSystem } from '@common/rpg/systems/effect';

// ---------- helpers ----------

function roundtrip(world: World): World {
    return Serialization.deserialize(Serialization.serialize(world)) as World;
}

function isSymbolKeyed(world: World, entityId: string, ctor: new (...a: any[]) => Component<any>): boolean {
    const entity = world.getEntity(entityId)!;
    for (const [, component] of entity) {
        if (component instanceof ctor) {
            return typeof component[COMPONENT_KEY] === 'symbol';
        }
    }
    return false;
}

// ---------- named-key round-trip ----------

describe('Serialization — named key', () => {
    it('preserves a string-keyed component', () => {
        const w = new World();
        const e = w.createEntity('player');
        e.add(new Stat({ value: 42 }), 'str');
        const w2 = roundtrip(w);
        expect(w2.getEntity('player')!.get(Stat, 'str')!.value).toBe(42);
    });

    it('preserves two components with different named keys', () => {
        const w = new World();
        const e = w.createEntity('player');
        e.add(new Stat({ value: 10 }), 'str');
        e.add(new Stat({ value: 5 }), 'int');
        const w2 = roundtrip(w);
        const e2 = w2.getEntity('player')!;
        expect(e2.get(Stat, 'str')!.value).toBe(10);
        expect(e2.get(Stat, 'int')!.value).toBe(5);
    });
});

// ---------- symbol-key round-trip ----------

describe('Serialization — anonymous (symbol) key', () => {
    it('restores a symbol-keyed component as symbol-keyed', () => {
        const w = new World();
        w.createEntity('e').add(new Stat({ value: 7 })); // no key → Symbol
        const w2 = roundtrip(w);
        expect(isSymbolKeyed(w2, 'e', Stat)).toBeTrue();
    });

    it('restored symbol-keyed component is found by type lookup', () => {
        const w = new World();
        const e = w.createEntity('e');
        e.add(new Stat({ value: 7 }));
        const w2 = roundtrip(w);
        expect(w2.getEntity('e')!.get(Stat)!.value).toBe(7);
    });

    it('two anonymous components of the same type both survive round-trip', () => {
        const w = new World();
        const e = w.createEntity('e');
        e.add(new Stat({ value: 3 }));
        e.add(new Stat({ value: 9 }));
        const w2 = roundtrip(w);
        const stats = w2.getEntity('e')!.getAll(Stat);
        expect(stats.length).toBe(2);
        expect(stats.map(s => s.value).sort()).toEqual([3, 9]);
    });

    it('anonymous and named component of the same type both survive round-trip', () => {
        const w = new World();
        const e = w.createEntity('e');
        e.add(new Stat({ value: 10 }), 'str');
        e.add(new Stat({ value: 20 }));          // Symbol key
        const w2 = roundtrip(w);
        const e2 = w2.getEntity('e')!;
        expect(e2.get(Stat, 'str')!.value).toBe(10);
        // anonymous one still reachable by type
        const all = e2.getAll(Stat);
        expect(all.length).toBe(2);
    });
});

// ---------- effect + stat round-trip ----------

describe('Serialization — Effect round-trip', () => {
    it('multiple anonymous Effects both survive round-trip', () => {
        const w = new World();
        w.addSystem(new EffectSystem());
        const e = w.createEntity('target');
        e.add(new Stat({ value: 10 }), 'str');
        e.add(new Effect({ targetKey: 'str', delta: 3 }));
        e.add(new Effect({ targetKey: 'str', delta: 7 }));

        const w2 = roundtrip(w);
        w2.addSystem(new EffectSystem());
        expect(w2.getEntity('target')!.getAll(Effect).length).toBe(2);
    });

    it('world globals survive round-trip', () => {
        const w = new World();
        w.globals.score = 99;
        w.globals.level = 3;
        const w2 = roundtrip(w);
        expect(w2.globals.score).toBe(99);
        expect(w2.globals.level).toBe(3);
    });

    it('entity counter continues from saved value', () => {
        const w = new World();
        w.createEntity();
        w.createEntity();
        const w2 = roundtrip(w);
        const e = w2.createEntity();
        expect(e.id).toBe('entity_3');
    });
});
