import { describe, expect, it, vi } from 'vitest';

// Pixi-зависимости — минимальные стабы: Engine в Node не инициализирует WebGL
// (init() не зовётся), но конструктор Renderer создаёт Application сразу.
vi.mock('pixi.js', () => {
    class Container {
        position = { set: () => undefined };
        scale = { set: () => undefined };
        visible = true;
        destroyed = false;
        addChild(..._c: unknown[]) {
            return this;
        }
        destroy(_opts?: unknown): void {}
    }
    class Graphics extends Container {}
    class Application {
        stage = new Container();
        canvas = { style: {} as Record<string, string> };
        renderer = { render: () => undefined };
        async init(): Promise<void> {}
        render(): void {}
        destroy(_a?: unknown, _b?: unknown): void {}
    }
    class Text extends Container {}
    return { Container, Graphics, Application, Text };
});

import { Engine } from '../Engine';
import type { Scene } from '../../scene/SceneManager';

function makeEngine(): Engine {
    return new Engine({
        virtualWidth: 480,
        virtualHeight: 270,
        scale: 2,
        background: 0x000000,
        parent: {} as HTMLElement,
        fixedFps: 60
    });
}

function makeScene(update: (dt: number) => void = () => {}, render: () => void = () => {}): Scene {
    return { enter: () => {}, exit: () => {}, update, render };
}

describe('Engine (шаги без цикла)', () => {
    it('stepTick: фиксированный шаг, dt = 1/fps в сцену, событие engine:tick', () => {
        const engine = makeEngine();
        const dts: number[] = [];
        void engine.scenes.push(makeScene((dt) => dts.push(dt)));
        const ticks: number[] = [];
        engine.events.on<{ tick: number }>('engine:tick', (p) => ticks.push(p!.tick));

        engine.stepTicks(3);

        expect(engine.tickCount).toBe(3);
        expect(dts).toEqual([1 / 60, 1 / 60, 1 / 60]);
        expect(ticks).toEqual([1, 2, 3]);
    });

    it('stepTicks(render: true) вызывает render сцены', () => {
        const engine = makeEngine();
        let renders = 0;
        void engine.scenes.push(makeScene(() => {}, () => renders++));

        engine.stepTicks(2, true);
        expect(renders).toBe(2);

        renders = 0;
        engine.stepTicks(2, false); // без рендера — только логика
        expect(renders).toBe(0);
    });

    it('tickCount копится между вызовами, fixedStep = 1/fps', () => {
        const engine = makeEngine();
        expect(engine.fixedStep).toBeCloseTo(1 / 60, 10);
        engine.stepTicks(5);
        expect(engine.tickCount).toBe(5);
        engine.stepTicks(2);
        expect(engine.tickCount).toBe(7);
    });

    it('исключение в шаге не оставляет счётчики вранья: тик не засчитан', () => {
        const engine = makeEngine();
        void engine.scenes.push(
            makeScene(() => {
                throw new Error('бум');
            })
        );
        expect(() => engine.stepTick()).toThrow('бум');
        // Счётчик и событие не продвинулись — шаг не доигран.
        expect(engine.tickCount).toBe(0);
    });
});