import { describe, it, expect } from 'bun:test';
import { World } from '@common/rpg/core/world';
import { Reputation, Factions } from '@common/rpg/components/faction';
import { resolveVariables } from '@common/rpg/utils/variables';

function world() { return new World(); }

describe('FactionMember', () => {
    it('join() adds membership, isMember() returns true', () => {
        const w = world();
        const e = w.createEntity();
        Factions.join(e, 'guards');
        expect(Factions.isMember(e, 'guards')).toBeTrue();
    });

    it('leave() removes membership, isMember() returns false', () => {
        const w = world();
        const e = w.createEntity();
        Factions.join(e, 'guards');
        Factions.leave(e, 'guards');
        expect(Factions.isMember(e, 'guards')).toBeFalse();
    });

    it('isMember() returns false when entity has no FactionMember', () => {
        const w = world();
        const e = w.createEntity();
        expect(Factions.isMember(e, 'guards')).toBeFalse();
    });

    it('getFactions() returns all factionIds', () => {
        const w = world();
        const e = w.createEntity();
        Factions.join(e, 'guards');
        Factions.join(e, 'merchants');
        expect(Factions.getFactions(e).sort()).toEqual(['guards', 'merchants']);
    });

    it('member variable resolves on world', () => {
        const w = world();
        const e = w.createEntity('player');
        Factions.join(e, 'guards');
        expect(resolveVariables(w)['player.FactionMember.guards']).toBeTrue();
    });
});

describe('Reputation', () => {
    it('getReputation() returns 0 when no component', () => {
        const w = world();
        const e = w.createEntity();
        expect(Factions.getReputation(e, 'guards')).toBe(0);
    });

    it('setReputation() creates component on first call', () => {
        const w = world();
        const e = w.createEntity();
        Factions.setReputation(e, 'guards', 50);
        expect(Factions.getReputation(e, 'guards')).toBe(50);
    });

    it('setReputation() updates score on second call without duplicate component', () => {
        const w = world();
        const e = w.createEntity();
        Factions.setReputation(e, 'guards', 50);
        Factions.setReputation(e, 'guards', 75);
        expect(Factions.getReputation(e, 'guards')).toBe(75);
        expect(e.getAll(Reputation).length).toBe(1);
    });

    it('adjustReputation() adds delta from zero default', () => {
        const w = world();
        const e = w.createEntity();
        Factions.adjustReputation(e, 'guards', 20);
        expect(Factions.getReputation(e, 'guards')).toBe(20);
    });

    it('adjustReputation() accumulates correctly', () => {
        const w = world();
        const e = w.createEntity();
        Factions.adjustReputation(e, 'guards', 20);
        Factions.adjustReputation(e, 'guards', -5);
        expect(Factions.getReputation(e, 'guards')).toBe(15);
    });

    it('Reputation component adjust() mutates score', () => {
        const w = world();
        const e = w.createEntity();
        Factions.setReputation(e, 'guards', 10);
        const comp = e.get(Reputation, (c) => c.factionId == 'guards')!;
        comp.adjust(30);
        expect(comp.score).toBe(40);
    });

    it('score variable resolves on world', () => {
        const w = world();
        const e = w.createEntity('player');
        Factions.setReputation(e, 'guards', 50);
        expect(resolveVariables(w)['player.Reputation.guards']).toBe(50);
    });
});

describe('Factions.getReputationBetween', () => {
    it('returns null when target has no factions', () => {
        const w = world();
        const observer = w.createEntity();
        const target = w.createEntity();
        expect(Factions.getReputationBetween(observer, target)).toBeNull();
    });

    it("returns observer's rep with target's sole faction", () => {
        const w = world();
        const observer = w.createEntity();
        const target = w.createEntity();
        Factions.join(target, 'guards');
        Factions.setReputation(observer, 'guards', 40);
        expect(Factions.getReputationBetween(observer, target)).toBe(40);
    });

    it('returns the minimum when target belongs to multiple factions', () => {
        const w = world();
        const observer = w.createEntity();
        const target = w.createEntity();
        Factions.join(target, 'guards');
        Factions.join(target, 'bandits');
        Factions.setReputation(observer, 'guards', 60);
        Factions.setReputation(observer, 'bandits', -30);
        expect(Factions.getReputationBetween(observer, target)).toBe(-30);
    });
});
