import { describe, it, expect } from 'vitest';
import { SpatialGrid } from '../core/spatial-index';

describe('SpatialGrid', () => {
	it('inserts and queries a single node', () => {
		const grid = new SpatialGrid(500);
		grid.insert('a', 100, 100, 200, 100);
		expect(grid.size).toBe(1);

		const result = grid.query({ minX: 0, minY: 0, maxX: 500, maxY: 500 });
		expect(result.has('a')).toBe(true);
	});

	it('returns empty set for query with no matches', () => {
		const grid = new SpatialGrid(500);
		grid.insert('a', 100, 100, 200, 100);

		const result = grid.query({ minX: 1000, minY: 1000, maxX: 2000, maxY: 2000 });
		expect(result.size).toBe(0);
	});

	it('queries multiple nodes', () => {
		const grid = new SpatialGrid(500);
		grid.insert('a', 0, 0, 100, 100);
		grid.insert('b', 200, 200, 100, 100);
		grid.insert('c', 5000, 5000, 100, 100);

		const result = grid.query({ minX: -50, minY: -50, maxX: 400, maxY: 400 });
		expect(result.has('a')).toBe(true);
		expect(result.has('b')).toBe(true);
		expect(result.has('c')).toBe(false);
	});

	it('updates a node position', () => {
		const grid = new SpatialGrid(500);
		grid.insert('a', 0, 0, 100, 100);

		// Move to far-away location
		grid.update('a', 5000, 5000, 100, 100);
		expect(grid.size).toBe(1);

		const near = grid.query({ minX: -100, minY: -100, maxX: 200, maxY: 200 });
		expect(near.has('a')).toBe(false);

		const far = grid.query({ minX: 4900, minY: 4900, maxX: 5200, maxY: 5200 });
		expect(far.has('a')).toBe(true);
	});

	it('skips grid update when position stays in same cells', () => {
		const grid = new SpatialGrid(500);
		grid.insert('a', 100, 100, 50, 50);

		// Small move within same cell
		grid.update('a', 110, 110, 50, 50);
		expect(grid.size).toBe(1);

		const result = grid.query({ minX: 0, minY: 0, maxX: 500, maxY: 500 });
		expect(result.has('a')).toBe(true);
	});

	it('removes a node', () => {
		const grid = new SpatialGrid(500);
		grid.insert('a', 0, 0, 100, 100);
		grid.insert('b', 200, 200, 100, 100);
		grid.remove('a');

		expect(grid.size).toBe(1);
		expect(grid.has('a')).toBe(false);
		expect(grid.has('b')).toBe(true);

		const result = grid.query({ minX: -100, minY: -100, maxX: 500, maxY: 500 });
		expect(result.has('a')).toBe(false);
		expect(result.has('b')).toBe(true);
	});

	it('remove on non-existent id is a no-op', () => {
		const grid = new SpatialGrid(500);
		grid.remove('nonexistent');
		expect(grid.size).toBe(0);
	});

	it('handles node spanning multiple cells', () => {
		const grid = new SpatialGrid(500);
		// Node from (400, 400) to (600, 600) spans 4 cells
		grid.insert('big', 400, 400, 200, 200);

		// Query top-left cell
		const tl = grid.query({ minX: 0, minY: 0, maxX: 499, maxY: 499 });
		expect(tl.has('big')).toBe(true);

		// Query bottom-right cell
		const br = grid.query({ minX: 500, minY: 500, maxX: 999, maxY: 999 });
		expect(br.has('big')).toBe(true);
	});

	it('clears all entries', () => {
		const grid = new SpatialGrid(500);
		grid.insert('a', 0, 0, 100, 100);
		grid.insert('b', 200, 200, 100, 100);

		grid.clear();
		expect(grid.size).toBe(0);
		expect(grid.has('a')).toBe(false);

		const result = grid.query({ minX: -1000, minY: -1000, maxX: 1000, maxY: 1000 });
		expect(result.size).toBe(0);
	});

	it('insert on existing id is treated as update', () => {
		const grid = new SpatialGrid(500);
		grid.insert('a', 0, 0, 100, 100);
		grid.insert('a', 5000, 5000, 100, 100);

		expect(grid.size).toBe(1);

		const near = grid.query({ minX: -100, minY: -100, maxX: 200, maxY: 200 });
		expect(near.has('a')).toBe(false);

		const far = grid.query({ minX: 4900, minY: 4900, maxX: 5200, maxY: 5200 });
		expect(far.has('a')).toBe(true);
	});

	it('handles negative coordinates', () => {
		const grid = new SpatialGrid(500);
		grid.insert('neg', -300, -300, 100, 100);

		const result = grid.query({ minX: -400, minY: -400, maxX: -100, maxY: -100 });
		expect(result.has('neg')).toBe(true);

		const miss = grid.query({ minX: 0, minY: 0, maxX: 500, maxY: 500 });
		expect(miss.has('neg')).toBe(false);
	});

	it('small cell size works correctly', () => {
		const grid = new SpatialGrid(50);
		grid.insert('a', 0, 0, 200, 200);
		grid.insert('b', 300, 300, 50, 50);

		const result = grid.query({ minX: -10, minY: -10, maxX: 210, maxY: 210 });
		expect(result.has('a')).toBe(true);
		expect(result.has('b')).toBe(false);
	});
});
