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

// Pixi-зависимости (Text из PixelText, Graphics, Container) — минимальные стабы.
vi.mock('pixi.js', () => {
    class Container {
        children: unknown[] = [];
        position = { set: () => undefined };
        visible = true;
        addChild(...c: unknown[]) {
            this.children.push(...c);
            return this;
        }
    }
    class Text {
        text = '';
        width = 30;
        height = 10;
        position = { set: () => undefined };
        constructor(opts?: { text?: string }) {
            this.text = opts?.text ?? '';
        }
    }
    class Graphics {
        clear() {
            return this;
        }
        rect() {
            return this;
        }
        fill() {
            return this;
        }
        stroke() {
            return this;
        }
    }
    return { Container, Text, Graphics };
});

import { FpsMeter } from '../FpsMeter';

/** Заглушка performance.now + keydown: клик по F3 и продвижение времени вручную. */
function installEnv() {
    let now = 0;
    const listeners: ((e: { code: string; preventDefault: () => void }) => void)[] = [];
    vi.stubGlobal('performance', { now: () => now });
    vi.stubGlobal('window', {
        addEventListener: (_: string, cb: (e: { code: string; preventDefault: () => void }) => void) => listeners.push(cb),
        removeEventListener: (_: string, cb: unknown) => {
            const i = listeners.indexOf(cb as never);
            if (i >= 0) listeners.splice(i, 1);
        }
    });
    return {
        advance: (ms: number) => {
            now += ms;
        },
        pressF3: () => {
            for (const cb of [...listeners]) cb({ code: 'F3', preventDefault: () => undefined });
        },
        listenerCount: () => listeners.length
    };
}

/** Текст счётчика (второй ребёнок view — label). */
function labelOf(meter: FpsMeter): string {
    return (meter.view.children[1] as unknown as { text: string }).text;
}

describe('FpsMeter', () => {
    afterEach(() => {
        vi.unstubAllGlobals();
    });

    it('скрыт по умолчанию; F3 показывает, повторное — прячет', () => {
        const env = installEnv();
        const meter = new FpsMeter();
        expect(meter.view.visible).toBe(false);

        env.pressF3();
        expect(meter.view.visible).toBe(true);
        env.pressF3();
        expect(meter.view.visible).toBe(false);

        meter.dispose();
        expect(env.listenerCount()).toBe(0);
    });

    it('считает кадры и обновляет текст раз в ~полсекунды', () => {
        const env = installEnv();
        const meter = new FpsMeter();
        meter.toggle(); // окно открыто на now = 0

        env.advance(100);
        meter.frame(); // кадр 1
        env.advance(16);
        meter.frame(); // кадр 2
        expect(labelOf(meter)).toBe('FPS --'); // окно не дозрело

        env.advance(420); // суммарно ~536 мс с показа
        meter.frame();
        expect(labelOf(meter)).toBe('FPS 6'); // 3 кадра за 536 мс

        meter.dispose();
    });

    it('после нового показа окно чистое: счётчики сброшены toggle-ом', () => {
        const env = installEnv();
        const meter = new FpsMeter();
        meter.toggle();
        env.advance(600);
        meter.frame(); // 1 кадр за 600 мс
        expect(labelOf(meter)).toBe('FPS 2');

        meter.toggle(); // скрыть
        env.advance(600);
        meter.frame(); // скрыт — не считается
        meter.toggle(); // показать заново: счётчики сброшены
        env.advance(600);
        meter.frame();
        expect(labelOf(meter)).toBe('FPS 2'); // без утечки прежних кадров

        meter.dispose();
    });
});