import { describe, expect, it } from 'vitest';
import { Updatables, type Updatable } from '../Updatables';

/** Фейковый тикаемый: считает вызовы, может пометить себя destroyed. */
function make(log: string[], name: string, destroyed = false): Updatable {
    return {
        destroyed,
        update(dt: number) {
            log.push(`${name}@${dt}`);
        }
    };
}

describe('Updatables', () => {
    it('тикает в порядке добавления', () => {
        const log: string[] = [];
        const r = new Updatables();
        r.add(make(log, 'a'));
        r.add(make(log, 'b'));
        r.update(1 / 60);
        expect(log).toEqual(['a@0.016666666666666666', 'b@0.016666666666666666']);
        expect(r.count).toBe(2);
    });

    it('add возвращает тот же объект (chaining)', () => {
        const u: Updatable = { update() {} };
        expect(new Updatables().add(u)).toBe(u);
    });

    it('add не дублирует и remove убирает', () => {
        const r = new Updatables();
        const u: Updatable = { update() {} };
        r.add(u);
        r.add(u);
        expect(r.count).toBe(1);
        r.remove(u);
        expect(r.count).toBe(0);
        r.remove(u); // отсутствие — не ошибка
    });

    it('destroyed-записи не тикаются и вычищаются после прохода', () => {
        const log: string[] = [];
        const r = new Updatables();
        r.add(make(log, 'zombie', true));
        r.add(make(log, 'alive'));
        r.update(0.1);
        expect(log).toEqual(['alive@0.1']); // разрушенный не тикается (transform обнулён)
        expect(r.count).toBe(1);
        r.update(0.1);
        expect(log).toEqual(['alive@0.1', 'alive@0.1']);
    });

    it('запись, разрушенная в чужом update, не тикается на следующем проходе', () => {
        const log: string[] = [];
        const r = new Updatables();
        const victim = make(log, 'victim');
        r.add(victim);
        r.add({
            update(dt: number) {
                log.push(`killer@${dt}`);
                victim.destroyed = true; // сцена разрушила вьюху между тиками
            }
        });
        r.update(0.1);
        expect(log).toEqual(['victim@0.1', 'killer@0.1']);
        r.update(0.1);
        expect(log).toEqual(['victim@0.1', 'killer@0.1', 'killer@0.1']); // victim молчит
        expect(r.count).toBe(1);
    });

    it('clear опустошает реестр', () => {
        const r = new Updatables();
        r.add({ update() {} });
        r.clear();
        expect(r.count).toBe(0);
    });
});