import { describe, it, expect, vi, afterEach } from 'vitest';
import { canvasMark, canvasWrap, setPerfEnabled } from '../core/perf';

describe('Performance Instrumentation', () => {
	afterEach(() => {
		setPerfEnabled(false);
	});

	it('canvasMark is a no-op when disabled', () => {
		const markSpy = vi.spyOn(performance, 'mark');
		const end = canvasMark('test');
		end();
		expect(markSpy).not.toHaveBeenCalled();
		markSpy.mockRestore();
	});

	it('canvasMark creates marks when enabled', () => {
		setPerfEnabled(true);
		const markSpy = vi.spyOn(performance, 'mark');
		const measureSpy = vi.spyOn(performance, 'measure');

		const end = canvasMark('test-op');
		expect(markSpy).toHaveBeenCalledWith('canvas:test-op');

		end();
		expect(measureSpy).toHaveBeenCalledWith('canvas:test-op', 'canvas:test-op');

		markSpy.mockRestore();
		measureSpy.mockRestore();
	});

	it('canvasWrap measures synchronous function', () => {
		setPerfEnabled(true);
		const markSpy = vi.spyOn(performance, 'mark');
		const measureSpy = vi.spyOn(performance, 'measure');

		const result = canvasWrap('sync-fn', () => 42);
		expect(result).toBe(42);
		expect(markSpy).toHaveBeenCalledWith('canvas:sync-fn');
		expect(measureSpy).toHaveBeenCalledWith('canvas:sync-fn', 'canvas:sync-fn');

		markSpy.mockRestore();
		measureSpy.mockRestore();
	});

	it('canvasWrap propagates exceptions', () => {
		setPerfEnabled(true);
		const measureSpy = vi.spyOn(performance, 'measure');

		expect(() => canvasWrap('throw-fn', () => {
			throw new Error('boom');
		})).toThrow('boom');

		// measure should still have been called in the finally block
		expect(measureSpy).toHaveBeenCalled();
		measureSpy.mockRestore();
	});

	it('setPerfEnabled toggles instrumentation', () => {
		const markSpy = vi.spyOn(performance, 'mark');

		canvasMark('off-test');
		expect(markSpy).not.toHaveBeenCalled();

		setPerfEnabled(true);
		canvasMark('on-test');
		expect(markSpy).toHaveBeenCalledWith('canvas:on-test');

		markSpy.mockRestore();
	});
});
