import { describe, it, expect, vi } from 'vitest';
import { createWheelHandler, type WheelZoomConfig } from '../gestures/useWheelZoom';

function makeConfig(overrides: Partial<WheelZoomConfig> = {}): WheelZoomConfig {
	const el = document.createElement('div');
	Object.defineProperty(el, 'getBoundingClientRect', {
		value: () => ({ left: 0, top: 0, width: 800, height: 600, right: 800, bottom: 600, x: 0, y: 0, toJSON: () => {} }),
	});

	return {
		ref: { current: el },
		minZoom: 0.1,
		maxZoom: 5,
		zoomSensitivity: 0.002,
		enableZoom: true,
		zoom: 1,
		pan: { x: 0, y: 0 },
		selectedNodeIds: new Set(),
		setZoom: vi.fn(),
		setPan: vi.fn(),
		...overrides,
	};
}

function makeWheelEvent(overrides: Partial<WheelEvent> = {}): any {
	return {
		preventDefault: vi.fn(),
		clientX: 400,
		clientY: 300,
		target: document.createElement('div'),
		...overrides,
	};
}

describe('createWheelHandler', () => {
	it('creates a handler function', () => {
		const handler = createWheelHandler(makeConfig());
		expect(typeof handler).toBe('function');
	});

	it('zooms in on negative delta (scroll up)', () => {
		const config = makeConfig();
		const handler = createWheelHandler(config);

		handler({ event: makeWheelEvent(), delta: [0, -100], pinching: false, memo: undefined });

		expect(config.setZoom).toHaveBeenCalled();
		expect(config.setPan).toHaveBeenCalled();
		// New zoom should be > 1 (zoomed in)
		const newZoom = (config.setZoom as ReturnType<typeof vi.fn>).mock.calls[0][0];
		expect(newZoom).toBeGreaterThan(1);
	});

	it('zooms out on positive delta (scroll down)', () => {
		const config = makeConfig();
		const handler = createWheelHandler(config);

		handler({ event: makeWheelEvent(), delta: [0, 100], pinching: false, memo: undefined });

		const newZoom = (config.setZoom as ReturnType<typeof vi.fn>).mock.calls[0][0];
		expect(newZoom).toBeLessThan(1);
	});

	it('clamps zoom to minZoom', () => {
		const config = makeConfig({ zoom: 0.15, minZoom: 0.1 });
		const handler = createWheelHandler(config);

		handler({ event: makeWheelEvent(), delta: [0, 10000], pinching: false, memo: undefined });

		const newZoom = (config.setZoom as ReturnType<typeof vi.fn>).mock.calls[0][0];
		expect(newZoom).toBeGreaterThanOrEqual(0.1);
	});

	it('clamps zoom to maxZoom', () => {
		const config = makeConfig({ zoom: 4.9, maxZoom: 5 });
		const handler = createWheelHandler(config);

		handler({ event: makeWheelEvent(), delta: [0, -10000], pinching: false, memo: undefined });

		const newZoom = (config.setZoom as ReturnType<typeof vi.fn>).mock.calls[0][0];
		expect(newZoom).toBeLessThanOrEqual(5);
	});

	it('does nothing when enableZoom is false', () => {
		const config = makeConfig({ enableZoom: false });
		const handler = createWheelHandler(config);

		handler({ event: makeWheelEvent(), delta: [0, -100], pinching: false, memo: undefined });

		expect(config.setZoom).not.toHaveBeenCalled();
	});

	it('does nothing when pinching', () => {
		const config = makeConfig();
		const handler = createWheelHandler(config);

		handler({ event: makeWheelEvent(), delta: [0, -100], pinching: true, memo: undefined });

		expect(config.setZoom).not.toHaveBeenCalled();
	});
});
